changelog shortlog tags changeset files revisions annotate raw

src/graphics.cc

changeset 9846: 1d90fc211872
parent:dfc68e6d8741
author: John W. Eaton <jwe@octave.org>
date: Sat Nov 21 21:44:51 2009 -0500 (21 hours ago)
permissions: -rw-r--r--
description: configure.ac: report freetype, fontconfig, and fltk cflags and libs info
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
1398void
1399graphics_object::set (const octave_value_list& args)
1400{
1401 int nargin = args.length ();
1402
1403 if (nargin == 0)
1404 rep->defaults ();
1405 else if (nargin % 2 == 0)
1406 {
1407 for (int i = 0; i < nargin; i += 2)
1408 {
1409 caseless_str name = args(i).string_value ();
1410
1411 if (! error_state)
1412 {
1413 octave_value val = args(i+1);
1414
1415 if (val.is_string ())
1416 {
1417 caseless_str tval = val.string_value ();
1418
1419 if (tval.compare ("default"))
1420 val = get_default (name);
1421 else if (tval.compare ("factory"))
1422 val = get_factory_default (name);
1423 }
1424
1425 if (error_state)
1426 break;
1427
1428 rep->set (name, val);
1429 }
1430 else
1431 error ("set: expecting argument %d to be a property name", i);
1432 }
1433 }
1434 else
1435 error ("set: invalid number of arguments");
1436}
1437
1438static double
1439make_handle_fraction (void)
1440{
1441 static double maxrand = RAND_MAX + 2.0;
1442
1443 return (rand () + 1.0) / maxrand;
1444}
1445
1446graphics_handle
1447gh_manager::get_handle (const std::string& go_name)
1448{
1449 graphics_handle retval;
1450
1451 if (go_name == "figure")
1452 {
1453 // Figure handles are positive integers corresponding to the
1454 // figure number.
1455
1456 // We always want the lowest unused figure number.
1457
1458 retval = 1;
1459
1460 while (handle_map.find (retval) != handle_map.end ())
1461 retval++;
1462 }
1463 else
1464 {
1465 // Other graphics handles are negative integers plus some random
1466 // fractional part. To avoid running out of integers, we
1467 // recycle the integer part but tack on a new random part each
1468 // time.
1469
1470 free_list_iterator p = handle_free_list.begin ();
1471
1472 if (p != handle_free_list.end ())
1473 {
1474 retval = *p;
1475 handle_free_list.erase (p);
1476 }
1477 else
1478 {
1479 retval = graphics_handle (next_handle);
1480
1481 next_handle = ceil (next_handle) - 1.0 - make_handle_fraction ();
1482 }
1483 }
1484
1485 return retval;
1486}
1487
1488void
1489gh_manager::do_free (const graphics_handle& h)
1490{
1491 if (h.ok ())
1492 {
1493 if (h.value () != 0)
1494 {
1495 iterator p = handle_map.find (h);
1496
1497 if (p != handle_map.end ())
1498 {
1499 base_properties& bp = p->second.get_properties ();
1500
1501 bp.set_beingdeleted (true);
1502
1503 bp.delete_children ();
1504
1505 octave_value val = bp.get_deletefcn ();
1506
1507 bp.execute_deletefcn ();
1508
1509 // notify backend
1510 graphics_backend backend = p->second.get_backend ();
1511 if (backend)
1512 backend.object_destroyed (p->second);
1513
1514 // Note: this will be valid only for first explicitly
1515 // deleted object. All its children will then have an
1516 // unknown backend.
1517
1518 // Graphics handles for non-figure objects are negative
1519 // integers plus some random fractional part. To avoid
1520 // running out of integers, we recycle the integer part
1521 // but tack on a new random part each time.
1522
1523 handle_map.erase (p);
1524
1525 if (h.value () < 0)
1526 handle_free_list.insert (ceil (h.value ()) - make_handle_fraction ());
1527 }
1528 else
1529 error ("graphics_handle::free: invalid object %g", h.value ());
1530 }
1531 else
1532 error ("graphics_handle::free: can't delete root figure");
1533 }
1534}
1535
1536gh_manager *gh_manager::instance = 0;
1537
1538static void
1539xset (const graphics_handle& h, const caseless_str& name,
1540 const octave_value& val)
1541{
1542 graphics_object obj = gh_manager::get_object (h);
1543 obj.set (name, val);
1544}
1545
1546static void
1547xset (const graphics_handle& h, const octave_value_list& args)
1548{
1549 if (args.length () > 0)
1550 {
1551 graphics_object obj = gh_manager::get_object (h);
1552 obj.set (args);
1553 }
1554}
1555
1556
1557static octave_value
1558xget (const graphics_handle& h, const caseless_str& name)
1559{
1560 graphics_object obj = gh_manager::get_object (h);
1561 return obj.get (name);
1562}
1563
1564static graphics_handle
1565reparent (const octave_value& ov, const std::string& who,
1566 const std::string& property, const graphics_handle& new_parent,
1567 bool adopt = true)
1568{
1569 graphics_handle h = octave_NaN;
1570
1571 double val = ov.double_value ();
1572
1573 if (! error_state)
1574 {
1575 h = gh_manager::lookup (val);
1576
1577 if (h.ok ())
1578 {
1579 graphics_object obj = gh_manager::get_object (h);
1580
1581 graphics_handle parent_h = obj.get_parent ();
1582
1583 graphics_object parent_obj = gh_manager::get_object (parent_h);
1584
1585 parent_obj.remove_child (h);
1586
1587 if (adopt)
1588 obj.set ("parent", new_parent.value ());
1589 else
1590 obj.reparent (new_parent);
1591 }
1592 else
1593 error ("%s: invalid graphics handle (= %g) for %s",
1594 who.c_str (), val, property.c_str ());
1595 }
1596 else
1597 error ("%s: expecting %s to be a graphics handle",
1598 who.c_str (), property.c_str ());
1599
1600 return h;
1601}
1602
1603// This function is NOT equivalent to the scripting language function gcf.
1604graphics_handle
1605gcf (void)
1606{
1607 octave_value val = xget (0, "currentfigure");
1608
1609 return val.is_empty () ? octave_NaN : val.double_value ();
1610}
1611
1612// This function is NOT equivalent to the scripting language function gca.
1613graphics_handle
1614gca (void)
1615{
1616 octave_value val = xget (gcf (), "currentaxes");
1617
1618 return val.is_empty () ? octave_NaN : val.double_value ();
1619}
1620
1621static void
1622adopt (const graphics_handle& p, const graphics_handle& h)
1623{
1624 graphics_object parent_obj = gh_manager::get_object (p);
1625
1626 parent_obj.adopt (h);
1627}
1628
1629static bool
1630is_handle (const graphics_handle& h)
1631{
1632 return h.ok ();
1633}
1634
1635static bool
1636is_handle (double val)
1637{
1638 graphics_handle h = gh_manager::lookup (val);
1639
1640 return h.ok ();
1641}
1642
1643static octave_value
1644is_handle (const octave_value& val)
1645{
1646 octave_value retval = false;
1647
1648 if (val.is_real_scalar () && is_handle (val.double_value ()))
1649 retval = true;
1650 else if (val.is_real_matrix ())
1651 {
1652 if (val.is_string ())
1653 retval = boolNDArray (val.dims (), false);
1654 else
1655 {
1656 const NDArray handles = val.array_value ();
1657
1658 if (! error_state)
1659 {
1660 boolNDArray result (handles.dims ());
1661
1662 for (octave_idx_type i = 0; i < handles.numel (); i++)
1663 result.xelem (i) = is_handle (handles (i));
1664
1665 retval = result;
1666 }
1667 }
1668 }
1669
1670 return retval;
1671}
1672
1673static bool
1674is_figure (double val)
1675{
1676 graphics_object obj = gh_manager::get_object (val);
1677
1678 return obj && obj.isa ("figure");
1679}
1680
1681static void
1682xcreatefcn (const graphics_handle& h)
1683{
1684 graphics_object obj = gh_manager::get_object (h);
1685 obj.get_properties ().execute_createfcn ();
1686}
1687
1688// ---------------------------------------------------------------------
1689
1690void
1691base_graphics_backend::property_changed (const graphics_handle& h, int id)
1692{
1693 graphics_object go = gh_manager::get_object (h);
1694
1695 property_changed (go, id);
1696}
1697
1698void
1699base_graphics_backend::object_created (const graphics_handle& h)
1700{
1701 graphics_object go = gh_manager::get_object (h);
1702
1703 object_created (go);
1704}
1705
1706void
1707base_graphics_backend::object_destroyed (const graphics_handle& h)
1708{
1709 graphics_object go = gh_manager::get_object (h);
1710
1711 object_destroyed (go);
1712}
1713// ---------------------------------------------------------------------
1714
1715static Matrix
1716maybe_set_children (const Matrix& kids, const octave_value& val)
1717{
1718 const Matrix new_kids = val.matrix_value ();
1719
1720 bool ok = true;
1721
1722 if (! error_state)
1723 {
1724 if (kids.numel () == new_kids.numel ())
1725 {
1726 Matrix t1 = kids;
1727 Matrix t2 = new_kids;
1728
1729 t1.sort ();
1730 t2.sort ();
1731
1732 if (t1 != t2)
1733 ok = false;
1734 }
1735 else
1736 ok = false;
1737
1738 if (! ok)
1739 error ("set: new children must be a permutation of existing children");
1740 }
1741 else
1742 {
1743 ok = false;
1744 error ("set: expecting children to be array of graphics handles");
1745 }
1746
1747 return ok ? new_kids : kids;
1748}
1749
1750void
1751base_properties::set_from_list (base_graphics_object& obj,
1752 property_list& defaults)
1753{
1754 std::string go_name = graphics_object_name ();
1755
1756 property_list::plist_map_const_iterator p = defaults.find (go_name);
1757
1758 if (p != defaults.end ())
1759 {
1760 const property_list::pval_map_type pval_map = p->second;
1761
1762 for (property_list::pval_map_const_iterator q = pval_map.begin ();
1763 q != pval_map.end ();
1764 q++)
1765 {
1766 std::string pname = q->first;
1767
1768 obj.set (pname, q->second);
1769
1770 if (error_state)
1771 {
1772 error ("error setting default property %s", pname.c_str ());
1773 break;
1774 }
1775 }
1776 }
1777}
1778
1779octave_value
1780base_properties::get_dynamic (const caseless_str& name) const
1781{
1782 octave_value retval;
1783
1784 std::map<caseless_str, property, cmp_caseless_str>::const_iterator it = all_props.find (name);
1785
1786 if (it != all_props.end ())
1787 retval = it->second.get ();
1788 else
1789 error ("get: unknown property \"%s\"", name.c_str ());
1790
1791 return retval;
1792}
1793
1794octave_value
1795base_properties::get_dynamic (bool all) const
1796{
1797 Octave_map m;
1798
1799 for (std::map<caseless_str, property, cmp_caseless_str>::const_iterator it = all_props.begin ();
1800 it != all_props.end (); ++it)
1801 if (all || ! it->second.is_hidden ())
1802 m.assign (it->second.get_name (), it->second.get ());
1803
1804 return m;
1805}
1806
1807std::map<std::string, std::set<std::string> > base_properties::all_dynamic_properties;
1808
1809std::set<std::string>
1810base_properties::dynamic_property_names (const std::string& cname)
1811{
1812 return all_dynamic_properties[cname];
1813}
1814
1815bool
1816base_properties::has_dynamic_property (const std::string& pname,
1817 const std::string& cname)
1818{
1819 const std::set<std::string>& dynprops = dynamic_property_names (cname);
1820
1821 return dynprops.find (pname) != dynprops.end ();
1822}
1823
1824void
1825base_properties::set_dynamic (const caseless_str& pname,
1826 const std::string& cname,
1827 const octave_value& val)
1828{
1829 std::map<caseless_str, property, cmp_caseless_str>::iterator it = all_props.find (pname);
1830
1831 if (it != all_props.end ())
1832 it->second.set (val);
1833 else
1834 error ("set: unknown property \"%s\"", pname.c_str ());
1835
1836 if (! error_state)
1837 {
1838 all_dynamic_properties[cname].insert (pname);
1839
1840 mark_modified ();
1841 }
1842}
1843
1844property
1845base_properties::get_property_dynamic (const caseless_str& name)
1846{
1847 std::map<caseless_str, property, cmp_caseless_str>::const_iterator it = all_props.find (name);
1848
1849 if (it == all_props.end ())
1850 {
1851 error ("get_property: unknown property \"%s\"", name.c_str ());
1852 return property ();
1853 }
1854 else
1855 return it->second;
1856}
1857
1858void
1859base_properties::remove_child (const graphics_handle& h)
1860{
1861 octave_idx_type k = -1;
1862 octave_idx_type n = children.numel ();
1863 for (octave_idx_type i = 0; i < n; i++)
1864 {
1865 if (h.value () == children(i))
1866 {
1867 k = i;
1868 break;
1869 }
1870 }
1871
1872 if (k >= 0)
1873 {
1874 Matrix new_kids (n-1, 1);
1875 octave_idx_type j = 0;
1876 for (octave_idx_type i = 0; i < n; i++)
1877 {
1878 if (i != k)
1879 new_kids(j++) = children(i);
1880 }
1881 children = new_kids;
1882 mark_modified ();
1883 }
1884}
1885
1886void
1887base_properties::set_parent (const octave_value& val)
1888{
1889 double tmp = val.double_value ();
1890
1891 graphics_handle new_parent = octave_NaN;
1892
1893 if (! error_state)
1894 {
1895 new_parent = gh_manager::lookup (tmp);
1896
1897 if (new_parent.ok ())
1898 {
1899 graphics_object parent_obj = gh_manager::get_object (get_parent ());
1900
1901 parent_obj.remove_child (__myhandle__);
1902
1903 parent = new_parent.as_octave_value ();
1904
1905 ::adopt (parent.handle_value (), __myhandle__);
1906 }
1907 else
1908 error ("set: invalid graphics handle (= %g) for parent", tmp);
1909 }
1910 else
1911 error ("set: expecting parent to be a graphics handle");
1912}
1913
1914void
1915base_properties::set_children (const octave_value& val)
1916{
1917 children = maybe_set_children (children, val);
1918}
1919
1920void
1921base_properties::mark_modified (void)
1922{
1923 __modified__ = "on";
1924 graphics_object parent_obj = gh_manager::get_object (get_parent ());
1925 if (parent_obj)
1926 parent_obj.mark_modified ();
1927}
1928
1929void
1930base_properties::override_defaults (base_graphics_object& obj)
1931{
1932 graphics_object parent_obj = gh_manager::get_object (get_parent ());
1933
1934 if (parent_obj)
1935 parent_obj.override_defaults (obj);
1936}
1937
1938void
1939base_properties::update_axis_limits (const std::string& axis_type) const
1940{
1941 graphics_object obj = gh_manager::get_object (__myhandle__);
1942
1943 if (obj)
1944 obj.update_axis_limits (axis_type);
1945}
1946
1947void
1948base_properties::delete_children (void)
1949{
1950 octave_idx_type n = children.numel ();
1951
1952 // A callback function might have already deleted the child,
1953 // so check before deleting
1954 for (octave_idx_type i = 0; i < n; i++)
1955 {
1956 graphics_object go = gh_manager::get_object (children(i));
1957
1958 if (go.valid_object ())
1959 gh_manager::free (children(i));
1960 }
1961}
1962
1963graphics_backend
1964base_properties::get_backend (void) const
1965{
1966 graphics_object go = gh_manager::get_object (get_parent ());
1967
1968 if (go)
1969 return go.get_backend ();
1970 else
1971 return graphics_backend ();
1972}
1973
1974void
1975base_properties::update_boundingbox (void)
1976{
1977 Matrix kids = get_children ();
1978
1979 for (int i = 0; i < kids.numel (); i++)
1980 {
1981 graphics_object go = gh_manager::get_object (kids(i));
1982
1983 if (go.valid_object ())
1984 go.get_properties ().update_boundingbox ();
1985 }
1986}
1987
1988void
1989base_properties::add_listener (const caseless_str& nm, const octave_value& v,
1990 listener_mode mode)
1991{
1992 property p = get_property (nm);
1993
1994 if (! error_state && p.ok ())
1995 p.add_listener (v, mode);
1996}
1997
1998void
1999base_properties::delete_listener (const caseless_str& nm,
2000 const octave_value& v, listener_mode mode)
2001{
2002 property p = get_property (nm);
2003
2004 if (! error_state && p.ok ())
2005 p.delete_listener (v, mode);
2006}
2007
2008// ---------------------------------------------------------------------
2009
2010class gnuplot_backend : public base_graphics_backend
2011{
2012public:
2013 gnuplot_backend (void)
2014 : base_graphics_backend ("gnuplot") { }
2015
2016 ~gnuplot_backend (void) { }
2017
2018 bool is_valid (void) const { return true; }
2019
2020 void object_destroyed (const graphics_object& go)
2021 {
2022 if (go.isa ("figure"))
2023 {
2024 const figure::properties& props =
2025 dynamic_cast<const figure::properties&> (go.get_properties ());
2026
2027 send_quit (props.get___plot_stream__ ());
2028 }
2029 }
2030
2031 void property_changed (const graphics_object& go, int id)
2032 {
2033 if (go.isa ("figure"))
2034 {
2035 graphics_object obj (go);
2036
2037 figure::properties& props =
2038 dynamic_cast<figure::properties&> (obj.get_properties ());
2039
2040 switch (id)
2041 {
2042 case base_properties::VISIBLE:
2043 if (! props.is_visible ())
2044 {
2045 send_quit (props.get___plot_stream__ ());
2046 props.set___plot_stream__ (Matrix ());
2047 props.set___enhanced__ (false);
2048 }
2049 break;
2050 }
2051 }
2052 }
2053
2054 void redraw_figure (const graphics_object& go) const
2055 {
2056 octave_value_list args;
2057 args(0) = go.get_handle ().as_octave_value ();
2058 feval ("gnuplot_drawnow", args);
2059 }
2060
2061 void print_figure (const graphics_object& go, const std::string& term,
2062 const std::string& file, bool mono,
2063 const std::string& debug_file) const
2064 {
2065 octave_value_list args;
2066 if (! debug_file.empty ())
2067 args(4) = debug_file;
2068 args(3) = mono;
2069 args(2) = file;
2070 args(1) = term;
2071 args(0) = go.get_handle ().as_octave_value ();
2072 feval ("gnuplot_drawnow", args);
2073 }
2074
2075 Matrix get_canvas_size (const graphics_handle&) const
2076 {
2077 Matrix sz (1, 2, 0.0);
2078 return sz;
2079 }
2080
2081 double get_screen_resolution (void) const
2082 { return 72.0; }
2083
2084 Matrix get_screen_size (void) const
2085 { return Matrix (1, 2, 0.0); }
2086
2087private:
2088 void send_quit (const octave_value& pstream) const
2089 {
2090 if (! pstream.is_empty ())
2091 {
2092 octave_value_list args;
2093 Matrix fids = pstream.matrix_value ();
2094
2095 if (! error_state)
2096 {
2097 args(1) = "\nquit;\n";
2098 args(0) = fids(0);
2099 feval ("fputs", args);
2100
2101 args.resize (1);
2102 feval ("fflush", args);
2103 feval ("pclose", args);
2104
2105 if (fids.numel () > 1)
2106 {
2107 args(0) = fids(1);
2108 feval ("pclose", args);
2109
2110 if (fids.numel () > 2)
2111 {
2112 args(0) = fids(2);
2113 feval ("waitpid", args);
2114 }
2115 }
2116 }
2117 }
2118 }
2119};
2120
2121graphics_backend
2122graphics_backend::default_backend (void)
2123{
2124 if (available_backends.size () == 0)
2125 register_backend (new gnuplot_backend ());
2126
2127 return available_backends["gnuplot"];
2128}
2129
2130std::map<std::string, graphics_backend> graphics_backend::available_backends;
2131
2132// ---------------------------------------------------------------------
2133
2134void
2135base_graphics_object::update_axis_limits (const std::string& axis_type)
2136{
2137 if (valid_object ())
2138 {
2139 graphics_object parent_obj = gh_manager::get_object (get_parent ());
2140
2141 if (parent_obj)
2142 parent_obj.update_axis_limits (axis_type);
2143 }
2144 else
2145 error ("base_graphics_object::update_axis_limits: invalid graphics object");
2146}
2147
2148void
2149base_graphics_object::remove_all_listeners (void)
2150{
2151 Octave_map m = get (true).map_value ();
2152
2153 for (Octave_map::const_iterator pa = m.begin (); pa != m.end (); pa++)
2154 {
2155 // FIXME -- there has to be a better way. I think we want to
2156 // ask whether it is OK to delete the listener for the given
2157 // property. How can we know in advance that it will be OK?
2158
2159 unwind_protect::frame_id_t uwp_frame = unwind_protect::begin_frame ();
2160
2161 unwind_protect::protect_var (discard_error_messages);
2162 unwind_protect::protect_var (error_state);
2163
2164 discard_error_messages = true;
2165
2166 property p = get_properties ().get_property (pa->first);
2167
2168 if (! error_state && p.ok ())
2169 p.delete_listener ();
2170
2171 unwind_protect::run_frame (uwp_frame);
2172 }
2173}
2174
2175// ---------------------------------------------------------------------
2176
2177#include "graphics-props.cc"
2178
2179// ---------------------------------------------------------------------
2180
2181void
2182root_figure::properties::set_currentfigure (const octave_value& v)
2183{
2184 graphics_handle val (v);
2185
2186 if (error_state)
2187 return;
2188
2189 if (xisnan (val.value ()) || is_handle (val))
2190 {
2191 currentfigure = val;
2192
2193 gh_manager::push_figure (val);
2194 }
2195 else
2196 gripe_set_invalid ("currentfigure");
2197}
2198
2199void
2200root_figure::properties::set_callbackobject (const octave_value& v)
2201{
2202 graphics_handle val (v);
2203
2204 if (error_state)
2205 return;
2206
2207 if (xisnan (val.value ()))
2208 {
2209 if (! cbo_stack.empty ())
2210 {
2211 val = cbo_stack.front ();
2212
2213 cbo_stack.pop_front ();
2214 }
2215
2216 callbackobject = val;
2217 }
2218 else if (is_handle (val))
2219 {
2220 if (get_callbackobject ().ok ())
2221 cbo_stack.push_front (get_callbackobject ());
2222
2223 callbackobject = val;
2224 }
2225 else
2226 gripe_set_invalid ("callbackobject");
2227}
2228
2229void
2230root_figure::properties::update_units (void)
2231{
2232 caseless_str xunits = get_units ();
2233
2234 Matrix ss = default_screensize ();
2235
2236 double dpi = get_screenpixelsperinch ();
2237
2238 if (xunits.compare ("inches"))
2239 {
2240 ss(0) = 0;
2241 ss(1) = 0;
2242 ss(2) /= dpi;
2243 ss(3) /= dpi;
2244 }
2245 else if (xunits.compare ("centimeters"))
2246 {
2247 ss(0) = 0;
2248 ss(1) = 0;
2249 ss(2) *= 2.54 / dpi;
2250 ss(3) *= 2.54 / dpi;
2251 }
2252 else if (xunits.compare ("normalized"))
2253 {
2254 ss = Matrix (1, 4, 1.0);
2255 }
2256 else if (xunits.compare ("points"))
2257 {
2258 ss(0) = 0;
2259 ss(1) = 0;
2260 ss(2) *= 72 / dpi;
2261 ss(3) *= 72 / dpi;
2262 }
2263
2264 set_screensize (ss);
2265}
2266
2267void
2268root_figure::properties::remove_child (const graphics_handle& gh)
2269{
2270 gh_manager::pop_figure (gh);
2271
2272 graphics_handle cf = gh_manager::current_figure ();
2273
2274 xset (0, "currentfigure", cf.value ());
2275
2276 base_properties::remove_child (gh);
2277}
2278
2279property_list
2280root_figure::factory_properties = root_figure::init_factory_properties ();
2281
2282// ---------------------------------------------------------------------
2283
2284void
2285figure::properties::set_currentaxes (const octave_value& v)
2286{
2287 graphics_handle val (v);
2288
2289 if (error_state)
2290 return;
2291
2292 if (xisnan (val.value ()) || is_handle (val))
2293 currentaxes = val;
2294 else
2295 gripe_set_invalid ("currentaxes");
2296}
2297
2298void
2299figure::properties::remove_child (const graphics_handle& gh)
2300{
2301 base_properties::remove_child (gh);
2302
2303 if (gh == currentaxes.handle_value ())
2304 {
2305 graphics_handle new_currentaxes;
2306
2307 for (octave_idx_type i = 0; i < children.numel (); i++)
2308 {
2309 graphics_handle kid = children(i);
2310
2311 graphics_object go = gh_manager::get_object (kid);
2312
2313 if (go.isa ("axes"))
2314 {
2315 new_currentaxes = kid;
2316 break;
2317 }
2318 }
2319
2320 currentaxes = new_currentaxes;
2321 }
2322}
2323
2324void
2325figure::properties::set_visible (const octave_value& val)
2326{
2327 std::string s = val.string_value ();
2328
2329 if (! error_state)
2330 {
2331 if (s == "on")
2332 xset (0, "currentfigure", __myhandle__.value ());
2333
2334 visible = val;
2335 }
2336}
2337
2338Matrix
2339figure::properties::get_boundingbox (bool) const
2340{
2341 Matrix screen_size = xget (0, "screensize").matrix_value ().extract_n (0, 2, 1, 2);
2342 Matrix pos;
2343
2344 pos = convert_position (get_position ().matrix_value (), get_units (),
2345 "pixels", screen_size);
2346
2347 pos(0)--;
2348 pos(1)--;
2349 pos(1) = screen_size(1) - pos(1) - pos(3);
2350
2351 return pos;
2352}
2353
2354void
2355figure::properties::set_boundingbox (const Matrix& bb)
2356{
2357 Matrix screen_size = xget (0, "screensize").matrix_value ().extract_n (0, 2, 1, 2);
2358 Matrix pos = bb;
2359
2360 pos(1) = screen_size(1) - pos(1) - pos(3);
2361 pos(1)++;
2362 pos(0)++;
2363 pos = convert_position (pos, "pixels", get_units (), screen_size);
2364
2365 set_position (pos);
2366}
2367
2368void
2369figure::properties::set_position (const octave_value& v)
2370{
2371 if (! error_state)
2372 {
2373 Matrix old_bb, new_bb;
2374
2375 old_bb = get_boundingbox ();
2376 position = v;
2377 new_bb = get_boundingbox ();
2378
2379 if (old_bb != new_bb)
2380 {
2381 if (old_bb(2) != new_bb(2) || old_bb(3) != new_bb(3))
2382 {
2383 execute_resizefcn ();
2384 update_boundingbox ();
2385 }
2386 }
2387
2388 mark_modified ();
2389 }
2390}
2391
2392std::string
2393figure::properties::get_title (void) const
2394{
2395 if (is_numbertitle ())
2396 {
2397 std::ostringstream os;
2398 std::string nm = get_name ();
2399
2400 os << "Figure " << __myhandle__.value ();
2401 if (! nm.empty ())
2402 os << ": " << get_name ();
2403
2404 return os.str ();
2405 }
2406 else
2407 return get_name ();
2408}
2409
2410octave_value
2411figure::get_default (const caseless_str& name) const
2412{
2413 octave_value retval = default_properties.lookup (name);
2414
2415 if (retval.is_undefined ())
2416 {
2417 graphics_handle parent = get_parent ();
2418 graphics_object parent_obj = gh_manager::get_object (parent);
2419
2420 retval = parent_obj.get_default (name);
2421 }
2422
2423 return retval;
2424}
2425
2426// ---------------------------------------------------------------------
2427
2428void
2429axes::properties::init (void)
2430{
2431 position.add_constraint (dim_vector (1, 4));
2432 position.add_constraint (dim_vector (0, 0));
2433 outerposition.add_constraint (dim_vector (1, 4));
2434 colororder.add_constraint (dim_vector (-1, 3));
2435 dataaspectratio.add_constraint (dim_vector (1, 3));
2436 plotboxaspectratio.add_constraint (dim_vector (1, 3));
2437 xlim.add_constraint (2);
2438 ylim.add_constraint (2);
2439 zlim.add_constraint (2);
2440 clim.add_constraint (2);
2441 alim.add_constraint (2);
2442 xtick.add_constraint (dim_vector (1, -1));
2443 ytick.add_constraint (dim_vector (1, -1));
2444 ztick.add_constraint (dim_vector (1, -1));
2445 Matrix vw (1, 2, 0);
2446 vw(1) = 90;
2447 view = vw;
2448 view.add_constraint (dim_vector (1, 2));
2449 cameraposition.add_constraint (dim_vector (1, 3));
2450 Matrix upv (1, 3, 0.0);
2451 upv(2) = 1.0;
2452 cameraupvector = upv;
2453 cameraupvector.add_constraint (dim_vector (1, 3));
2454 currentpoint.add_constraint (dim_vector (2, 3));
2455 ticklength.add_constraint (dim_vector (1, 2));
2456 tightinset.add_constraint (dim_vector (1, 4));
2457
2458 x_zlim.resize (1, 2);
2459
2460 sx = "linear";
2461 sy = "linear";
2462 sz = "linear";
2463
2464 calc_ticklabels (xtick, xticklabel, xscale.is ("log"));
2465 calc_ticklabels (ytick, yticklabel, yscale.is ("log"));
2466 calc_ticklabels (ztick, zticklabel, zscale.is ("log"));
2467
2468 xset (xlabel.handle_value (), "handlevisibility", "off");
2469 xset (ylabel.handle_value (), "handlevisibility", "off");
2470 xset (zlabel.handle_value (), "handlevisibility", "off");
2471 xset (title.handle_value (), "handlevisibility", "off");
2472
2473 xset (xlabel.handle_value (), "horizontalalignment", "center");
2474 xset (ylabel.handle_value (), "horizontalalignment", "center");
2475 xset (zlabel.handle_value (), "horizontalalignment", "right");
2476 xset (title.handle_value (), "horizontalalignment", "center");
2477
2478 xset (xlabel.handle_value (), "verticalalignment", "cap");
2479 xset (ylabel.handle_value (), "verticalalignment", "bottom");
2480 xset (title.handle_value (), "verticalalignment", "bottom");
2481
2482 xset (ylabel.handle_value (), "rotation", 90.0);
2483 xset (zlabel.handle_value (), "visible", "off");
2484
2485 xset (xlabel.handle_value (), "clipping", "off");
2486 xset (ylabel.handle_value (), "clipping", "off");
2487 xset (zlabel.handle_value (), "clipping", "off");
2488 xset (title.handle_value (), "clipping", "off");
2489
2490 adopt (xlabel.handle_value ());
2491 adopt (ylabel.handle_value ());
2492 adopt (zlabel.handle_value ());
2493 adopt (title.handle_value ());
2494}
2495
2496void
2497axes::properties::sync_positions (void)
2498{
2499#if 0
2500 // FIXME -- this should take font metrics into consideration,
2501 // and also the fact that the colorbox leaves the outerposition
2502 // alone but alters the position. For now just don't adjust the
2503 // positions relative to each other.
2504
2505 if (activepositionproperty.is ("outerposition"))
2506 {
2507 Matrix outpos = outerposition.get ().matrix_value ();
2508 Matrix defpos = default_axes_position ();
2509 Matrix pos(outpos);
2510 pos(0) = outpos(0) + defpos(0) * outpos(2);
2511 pos(1) = outpos(1) + defpos(1) * outpos(3);
2512 pos(2) = outpos(2) * defpos(2);
2513 pos(3) = outpos(3) * defpos(3);
2514 position = pos;
2515 }
2516 else
2517 {
2518 Matrix pos = position.get ().matrix_value ();
2519 pos(0) -= pos(2)*0.05;
2520 pos(1) -= pos(3)*0.05;
2521 pos(2) *= 1.1;
2522 pos(3) *= 1.1;
2523 outerposition = pos;
2524 }
2525#endif
2526
2527 update_transform ();
2528}
2529
2530void
2531axes::properties::set_text_child (handle_property& hp,
2532 const std::string& who,
2533 const octave_value& v)
2534{
2535 graphics_handle val = ::reparent (v, "set", who, __myhandle__, false);
2536
2537 if (! error_state)
2538 {
2539 xset (val, "handlevisibility", "off");
2540
2541 gh_manager::free (hp.handle_value ());
2542
2543 base_properties::remove_child (hp.handle_value ());
2544
2545 hp = val;
2546
2547 adopt (hp.handle_value ());
2548 }
2549}
2550
2551void
2552axes::properties::set_xlabel (const octave_value& v)
2553{
2554 set_text_child (xlabel, "xlabel", v);
2555}
2556
2557void
2558axes::properties::set_ylabel (const octave_value& v)
2559{
2560 set_text_child (ylabel, "ylabel", v);
2561}
2562
2563void
2564axes::properties::set_zlabel (const octave_value& v)
2565{
2566 set_text_child (zlabel, "zlabel", v);
2567}
2568
2569void
2570axes::properties::set_title (const octave_value& v)
2571{
2572 set_text_child (title, "title", v);
2573}
2574
2575void
2576axes::properties::set_defaults (base_graphics_object& obj,
2577 const std::string& mode)
2578{
2579 box = "on";
2580 key = "off";
2581 keybox = "off";
2582 keyreverse = "off";
2583 keypos = 1.0;
2584 colororder = default_colororder ();
2585 dataaspectratio = Matrix (1, 3, 1.0);
2586 dataaspectratiomode = "auto";
2587 layer = "bottom";
2588
2589 Matrix tlim (1, 2, 0.0);
2590 tlim(1) = 1;
2591 xlim = tlim;
2592 ylim = tlim;
2593 zlim = tlim;
2594
2595 Matrix cl (1, 2, 0);
2596 cl(1) = 1;
2597 clim = cl;
2598
2599 xlimmode = "auto";
2600 ylimmode = "auto";
2601 zlimmode = "auto";
2602 climmode = "auto";
2603
2604 xgrid = "off";
2605 ygrid = "off";
2606 zgrid = "off";
2607 xminorgrid = "off";
2608 yminorgrid = "off";
2609 zminorgrid = "off";
2610 xtick = Matrix ();
2611 ytick = Matrix ();
2612 ztick = Matrix ();
2613 xtickmode = "auto";
2614 ytickmode = "auto";
2615 ztickmode = "auto";
2616 xticklabel = "";
2617 yticklabel = "";
2618 zticklabel = "";
2619 xticklabelmode = "auto";
2620 yticklabelmode = "auto";
2621 zticklabelmode = "auto";
2622 color = color_values (1, 1, 1);
2623 xcolor = color_values ("black");
2624 ycolor = color_values ("black");
2625 zcolor = color_values ("black");
2626 xscale = "linear";
2627 yscale = "linear";
2628 zscale = "linear";
2629 xdir = "normal";
2630 ydir = "normal";
2631 zdir = "normal";
2632 yaxislocation = "left";
2633 xaxislocation = "bottom";
2634
2635 // Note: camera properties will be set through update_transform
2636 camerapositionmode = "auto";
2637 cameratargetmode = "auto";
2638 cameraupvectormode = "auto";
2639 cameraviewanglemode = "auto";
2640 plotboxaspectratio = Matrix (1, 3, 1.0);
2641 drawmode = "normal";
2642 gridlinestyle = ":";
2643 linestyleorder = "-";
2644 linewidth = 0.5;
2645 minorgridlinestyle = ":";
2646 // Note: plotboxaspectratio will be set through update_aspectratiors
2647 plotboxaspectratiomode = "auto";
2648 projection = "orthographic";
2649 tickdir = "in";
2650 tickdirmode = "auto";
2651 ticklength = default_axes_ticklength ();
2652 tightinset = Matrix (1, 4, 0.0);
2653
2654 sx = "linear";
2655 sy = "linear";
2656 sz = "linear";
2657
2658 Matrix tview (1, 2, 0.0);
2659 tview(1) = 90;
2660 view = tview;
2661
2662 visible = "on";
2663 nextplot = "replace";
2664
2665 if (mode != "replace")
2666 {
2667 fontangle = "normal";
2668 fontname = OCTAVE_DEFAULT_FONTNAME;
2669 fontsize = 12;
2670 fontunits = "points";
2671 fontweight = "normal";
2672
2673 Matrix touterposition (1, 4, 0.0);
2674 touterposition(2) = 1;
2675 touterposition(3) = 1;
2676 outerposition = touterposition;
2677
2678 position = default_axes_position ();
2679
2680 activepositionproperty = "outerposition";
2681 }
2682
2683 delete_children ();
2684
2685 children = Matrix ();
2686
2687 xlabel = gh_manager::make_graphics_handle ("text", __myhandle__, false);
2688 ylabel = gh_manager::make_graphics_handle ("text", __myhandle__, false);
2689 zlabel = gh_manager::make_graphics_handle ("text", __myhandle__, false);
2690 title = gh_manager::make_graphics_handle ("text", __myhandle__, false);
2691
2692 xset (xlabel.handle_value (), "handlevisibility", "off");
2693 xset (ylabel.handle_value (), "handlevisibility", "off");
2694 xset (zlabel.handle_value (), "handlevisibility", "off");
2695 xset (title.handle_value (), "handlevisibility", "off");
2696
2697 xset (xlabel.handle_value (), "horizontalalignment", "center");
2698 xset (ylabel.handle_value (), "horizontalalignment", "center");
2699 xset (zlabel.handle_value (), "horizontalalignment", "right");
2700 xset (title.handle_value (), "horizontalalignment", "center");
2701
2702 xset (xlabel.handle_value (), "verticalalignment", "cap");
2703 xset (ylabel.handle_value (), "verticalalignment", "bottom");
2704 xset (title.handle_value (), "verticalalignment", "bottom");
2705
2706 xset (ylabel.handle_value (), "rotation", 90.0);
2707 xset (zlabel.handle_value (), "visible", "off");
2708
2709 xset (xlabel.handle_value (), "clipping", "off");
2710 xset (ylabel.handle_value (), "clipping", "off");
2711 xset (zlabel.handle_value (), "clipping", "off");
2712 xset (title.handle_value (), "clipping", "off");
2713
2714 adopt (xlabel.handle_value ());
2715 adopt (ylabel.handle_value ());
2716 adopt (zlabel.handle_value ());
2717 adopt (title.handle_value ());
2718
2719 update_transform ();
2720
2721 override_defaults (obj);
2722}
2723
2724void
2725axes::properties::delete_text_child (handle_property& hp)
2726{
2727 graphics_handle h = hp.handle_value ();
2728
2729 if (h.ok ())
2730 {
2731 graphics_object go = gh_manager::get_object (h);
2732
2733 if (go.valid_object ())
2734 gh_manager::free (h);
2735
2736 base_properties::remove_child (h);
2737 }
2738
2739 // FIXME -- is it necessary to check whether the axes object is
2740 // being deleted now? I think this function is only called when an
2741 // individual child object is delete and not when the parent axes
2742 // object is deleted.
2743
2744 if (! is_beingdeleted ())
2745 {
2746 hp = gh_manager::make_graphics_handle ("text", __myhandle__, false);
2747
2748 xset (hp.handle_value (), "handlevisibility", "off");
2749
2750 adopt (hp.handle_value ());
2751 }
2752}
2753
2754void
2755axes::properties::remove_child (const graphics_handle& h)
2756{
2757 if (xlabel.handle_value ().ok () && h == xlabel.handle_value ())
2758 delete_text_child (xlabel);
2759 else if (ylabel.handle_value ().ok () && h == ylabel.handle_value ())
2760 delete_text_child (ylabel);
2761 else if (zlabel.handle_value ().ok () && h == zlabel.handle_value ())
2762 delete_text_child (zlabel);
2763 else if (title.handle_value ().ok () && h == title.handle_value ())
2764 delete_text_child (title);
2765 else
2766 base_properties::remove_child (h);
2767}
2768
2769Matrix
2770base_properties::get_children (void) const
2771{
2772 Matrix retval = children;
2773
2774 graphics_object go = gh_manager::get_object (0);
2775
2776 root_figure::properties& props =
2777 dynamic_cast<root_figure::properties&> (go.get_properties ());
2778
2779 if (! props.is_showhiddenhandles ())
2780 {
2781 octave_idx_type k = 0;
2782
2783 for (octave_idx_type i = 0; i < children.numel (); i++)
2784 {
2785 graphics_handle kid = children (i);
2786
2787 if (gh_manager::is_handle_visible (kid))
2788 retval(k++) = children(i);
2789 }
2790
2791 retval.resize (k, 1);
2792 }
2793
2794 return retval;;
2795}
2796
2797inline Matrix
2798xform_matrix (void)
2799{
2800 Matrix m (4, 4, 0.0);
2801 for (int i = 0; i < 4; i++)
2802 m(i,i) = 1;
2803 return m;
2804}
2805
2806inline ColumnVector
2807xform_vector (void)
2808{
2809 ColumnVector v (4, 0.0);
2810 v(3) = 1;
2811 return v;
2812}
2813
2814inline ColumnVector
2815xform_vector (double x, double y, double z)
2816{
2817 ColumnVector v (4, 1.0);
2818 v(0) = x; v(1) = y; v(2) = z;
2819 return v;
2820}
2821
2822inline ColumnVector
2823transform (const Matrix& m, double x, double y, double z)
2824{
2825 return (m * xform_vector (x, y, z));
2826}
2827
2828inline Matrix
2829xform_scale (double x, double y, double z)
2830{
2831 Matrix m (4, 4, 0.0);
2832 m(0,0) = x; m(1,1) = y; m(2,2) = z; m(3,3) = 1;
2833 return m;
2834}
2835
2836inline Matrix
2837xform_translate (double x, double y, double z)
2838{
2839 Matrix m = xform_matrix ();
2840 m(0,3) = x; m(1,3) = y; m(2,3) = z; m(3,3) = 1;
2841 return m;
2842}
2843
2844inline void
2845scale (Matrix& m, double x, double y, double z)
2846{
2847 m = m * xform_scale (x, y, z);
2848}
2849
2850inline void
2851translate (Matrix& m, double x, double y, double z)
2852{
2853 m = m * xform_translate (x, y, z);
2854}
2855
2856inline void
2857xform (ColumnVector& v, const Matrix& m)
2858{
2859 v = m*v;
2860}
2861
2862inline void
2863scale (ColumnVector& v, double x, double y, double z)
2864{
2865 v(0) *= x;
2866 v(1) *= y;
2867 v(2) *= z;
2868}
2869
2870inline void
2871translate (ColumnVector& v, double x, double y, double z)
2872{
2873 v(0) += x;
2874 v(1) += y;
2875 v(2) += z;
2876}
2877
2878inline void
2879normalize (ColumnVector& v)
2880{
2881 double fact = 1.0/sqrt(v(0)*v(0)+v(1)*v(1)+v(2)*v(2));
2882 scale (v, fact, fact, fact);
2883}
2884
2885inline double
2886dot (const ColumnVector& v1, const ColumnVector& v2)
2887{
2888 return (v1(0)*v2(0)+v1(1)*v2(1)+v1(2)*v2(2));
2889}
2890
2891inline double
2892norm (const ColumnVector& v)
2893{
2894 return sqrt (dot (v, v));
2895}
2896
2897inline ColumnVector
2898cross (const ColumnVector& v1, const ColumnVector& v2)
2899{
2900 ColumnVector r = xform_vector ();
2901 r(0) = v1(1)*v2(2)-v1(2)*v2(1);
2902 r(1) = v1(2)*v2(0)-v1(0)*v2(2);
2903 r(2) = v1(0)*v2(1)-v1(1)*v2(0);
2904 return r;
2905}
2906
2907inline Matrix
2908unit_cube (void)
2909{
2910 static double data[32] = {
2911 0,0,0,1,
2912 1,0,0,1,
2913 0,1,0,1,
2914 0,0,1,1,
2915 1,1,0,1,
2916 1,0,1,1,
2917 0,1,1,1,
2918 1,1,1,1};
2919 Matrix m (4, 8);
2920 memcpy (m.fortran_vec (), data, sizeof(double)*32);
2921 return m;
2922}
2923
2924inline ColumnVector
2925cam2xform (const Array<double>& m)
2926{
2927 ColumnVector retval (4, 1.0);
2928 memcpy (retval.fortran_vec (), m.fortran_vec (), sizeof(double)*3);
2929 return retval;
2930}
2931
2932inline RowVector
2933xform2cam (const ColumnVector& v)
2934{
2935 return v.extract_n (0, 3).transpose ();
2936}
2937
2938void
2939axes::properties::update_camera (void)
2940{
2941 double xd = (xdir_is ("normal") ? 1 : -1);
2942 double yd = (ydir_is ("normal") ? 1 : -1);
2943 double zd = (zdir_is ("normal") ? 1 : -1);
2944
2945 Matrix xlimits = sx.scale (get_xlim ().matrix_value ());
2946 Matrix ylimits = sy.scale (get_ylim ().matrix_value ());
2947 Matrix zlimits = sz.scale (get_zlim ().matrix_value ());
2948
2949 double xo = xlimits(xd > 0 ? 0 : 1);
2950 double yo = ylimits(yd > 0 ? 0 : 1);
2951 double zo = zlimits(zd > 0 ? 0 : 1);
2952
2953 Matrix pb = get_plotboxaspectratio ().matrix_value ();
2954
2955 bool autocam = (camerapositionmode_is ("auto")
2956 && cameratargetmode_is ("auto")
2957 && cameraupvectormode_is ("auto")
2958 && cameraviewanglemode_is ("auto"));
2959 bool dowarp = (autocam && dataaspectratiomode_is("auto")
2960 && plotboxaspectratiomode_is ("auto"));
2961
2962 ColumnVector c_eye (xform_vector ());
2963 ColumnVector c_center (xform_vector ());
2964 ColumnVector c_upv (xform_vector ());
2965
2966 if (cameratargetmode_is ("auto"))
2967 {
2968 c_center(0) = (xlimits(0)+xlimits(1))/2;
2969 c_center(1) = (ylimits(0)+ylimits(1))/2;
2970 c_center(2) = (zlimits(0)+zlimits(1))/2;
2971
2972 cameratarget = xform2cam (c_center);
2973 }
2974 else
2975 c_center = cam2xform (get_cameratarget ().matrix_value ());
2976
2977 if (camerapositionmode_is ("auto"))
2978 {
2979 Matrix tview = get_view ().matrix_value ();
2980 double az = tview(0), el = tview(1);
2981 double d = 5*sqrt(pb(0)*pb(0)+pb(1)*pb(1)+pb(2)*pb(2));
2982
2983 if (el == 90 || el == -90)
2984 c_eye(2) = d*signum(el);
2985 else
2986 {
2987 az *= M_PI/180.0;
2988 el *= M_PI/180.0;
2989 c_eye(0) = d*cos(el)*sin(az);
2990 c_eye(1) = -d*cos(el)*cos(az);
2991 c_eye(2) = d*sin(el);
2992 }
2993 c_eye(0) = c_eye(0)*(xlimits(1)-xlimits(0))/(xd*pb(0))+c_center(0);
2994 c_eye(1) = c_eye(1)*(ylimits(1)-ylimits(0))/(yd*pb(1))+c_center(1);
2995 c_eye(2) = c_eye(2)*(zlimits(1)-zlimits(0))/(zd*pb(2))+c_center(2);
2996
2997 cameraposition = xform2cam (c_eye);
2998 }
2999 else
3000 c_eye = cam2xform (get_cameraposition ().matrix_value ());
3001
3002 if (cameraupvectormode_is ("auto"))
3003 {
3004 Matrix tview = get_view ().matrix_value ();
3005 double az = tview(0), el = tview(1);
3006
3007 if (el == 90 || el == -90)
3008 {
3009 c_upv(0) = -sin(az*M_PI/180.0)*(xlimits(1)-xlimits(0))/pb(0);
3010 c_upv(1) = cos(az*M_PI/180.0)*(ylimits(1)-ylimits(0))/pb(1);
3011 }
3012 else
3013 c_upv(2) = 1;
3014
3015 cameraupvector = xform2cam (c_upv);
3016 }
3017 else
3018 c_upv = cam2xform (get_cameraupvector ().matrix_value ());
3019
3020 Matrix x_view = xform_matrix ();
3021 Matrix x_projection = xform_matrix ();
3022 Matrix x_viewport = xform_matrix ();
3023 Matrix x_normrender = xform_matrix ();
3024 Matrix x_pre = xform_matrix ();
3025
3026 x_render = xform_matrix ();
3027 x_render_inv = xform_matrix ();
3028
3029 scale (x_pre, pb(0), pb(1), pb(2));
3030 translate (x_pre, -0.5, -0.5, -0.5);
3031 scale (x_pre, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
3032 zd/(zlimits(1)-zlimits(0)));
3033 translate (x_pre, -xo, -yo, -zo);
3034
3035 xform (c_eye, x_pre);
3036 xform (c_center, x_pre);
3037 scale (c_upv, pb(0)/(xlimits(1)-xlimits(0)), pb(1)/(ylimits(1)-ylimits(0)),
3038 pb(2)/(zlimits(1)-zlimits(0)));
3039 translate (c_center, -c_eye(0), -c_eye(1), -c_eye(2));
3040
3041 ColumnVector F (c_center), f (F), UP (c_upv);
3042 normalize (f);
3043 normalize (UP);
3044
3045 if (std::abs (dot (f, UP)) > 1e-15)
3046 {
3047 double fa = 1/sqrt(1-f(2)*f(2));
3048 scale (UP, fa, fa, fa);
3049 }
3050
3051 ColumnVector s = cross (f, UP);
3052 ColumnVector u = cross (s, f);
3053
3054 scale (x_view, 1, 1, -1);
3055 Matrix l = xform_matrix ();
3056 l(0,0) = s(0); l(0,1) = s(1); l(0,2) = s(2);
3057 l(1,0) = u(0); l(1,1) = u(1); l(1,2) = u(2);
3058 l(2,0) = -f(0); l(2,1) = -f(1); l(2,2) = -f(2);
3059 x_view = x_view * l;
3060 translate (x_view, -c_eye(0), -c_eye(1), -c_eye(2));
3061 scale (x_view, pb(0), pb(1), pb(2));
3062 translate (x_view, -0.5, -0.5, -0.5);
3063
3064 Matrix x_cube = x_view * unit_cube ();
3065 ColumnVector cmin = x_cube.row_min (), cmax = x_cube.row_max ();
3066 double xM = cmax(0)-cmin(0);
3067 double yM = cmax(1)-cmin(1);
3068
3069 Matrix bb = get_boundingbox (true);
3070
3071 double v_angle;
3072
3073 if (cameraviewanglemode_is ("auto"))
3074 {
3075 double af;
3076
3077 // FIXME -- was this really needed? When compared to Matlab, it
3078 // does not seem to be required. Need investigation with concrete
3079 // backend to see results visually.
3080 if (false && dowarp)
3081 af = 1.0 / (xM > yM ? xM : yM);
3082 else
3083 {
3084 if ((bb(2)/bb(3)) > (xM/yM))
3085 af = 1.0 / yM;
3086 else
3087 af = 1.0 / xM;
3088 }
3089 v_angle = 2 * (180.0 / M_PI) * atan (1 / (2 * af * norm (F)));
3090
3091 cameraviewangle = v_angle;
3092 }
3093 else
3094 v_angle = get_cameraviewangle ();
3095
3096 double pf = 1 / (2 * tan ((v_angle / 2) * M_PI / 180.0) * norm (F));
3097 scale (x_projection, pf, pf, 1);
3098
3099 if (dowarp)
3100 {
3101 xM *= pf;
3102 yM *= pf;
3103 translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
3104 scale (x_viewport, bb(2)/xM, -bb(3)/yM, 1);
3105 }
3106 else
3107 {
3108 double pix = 1;
3109 if (autocam)
3110 {
3111 if ((bb(2)/bb(3)) > (xM/yM))
3112 pix = bb(3);
3113 else
3114 pix = bb(2);
3115 }
3116 else
3117 pix = (bb(2) < bb(3) ? bb(2) : bb(3));
3118 translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
3119 scale (x_viewport, pix, -pix, 1);
3120 }
3121
3122 x_normrender = x_viewport * x_projection * x_view;
3123
3124 x_cube = x_normrender * unit_cube ();
3125 cmin = x_cube.row_min ();
3126 cmax = x_cube.row_max ();
3127 x_zlim.resize (1, 2);
3128 x_zlim(0) = cmin(2);
3129 x_zlim(1) = cmax(2);
3130
3131 x_render = x_normrender;
3132 scale (x_render, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
3133 zd/(zlimits(1)-zlimits(0)));
3134 translate (x_render, -xo, -yo, -zo);
3135
3136 x_viewtransform = x_view;
3137 x_projectiontransform = x_projection;
3138 x_viewporttransform = x_viewport;
3139 x_normrendertransform = x_normrender;
3140 x_rendertransform = x_render;
3141
3142 x_render_inv = x_render.inverse ();
3143
3144 // Note: these matrices are a slight modified version of the regular
3145 // matrices, more suited for OpenGL rendering (x_gl_mat1 => light
3146 // => x_gl_mat2)
3147 x_gl_mat1 = x_view;
3148 scale (x_gl_mat1, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
3149 zd/(zlimits(1)-zlimits(0)));
3150 translate (x_gl_mat1, -xo, -yo, -zo);
3151 x_gl_mat2 = x_viewport * x_projection;
3152}
3153
3154void
3155axes::properties::update_aspectratios (void)
3156{
3157 Matrix xlimits = get_xlim ().matrix_value ();
3158 Matrix ylimits = get_ylim ().matrix_value ();
3159 Matrix zlimits = get_zlim ().matrix_value ();
3160
3161 double dx = (xlimits(1)-xlimits(0));
3162 double dy = (ylimits(1)-ylimits(0));
3163 double dz = (zlimits(1)-zlimits(0));
3164
3165 if (dataaspectratiomode_is ("auto"))
3166 {
3167 double dmin = xmin (xmin (dx, dy), dz);
3168 Matrix da (1, 3, 0.0);
3169
3170 da(0) = dx/dmin;
3171 da(1) = dy/dmin;
3172 da(2) = dz/dmin;
3173
3174 dataaspectratio = da;
3175 }
3176
3177 if (plotboxaspectratiomode_is ("auto"))
3178 {
3179 if (dataaspectratiomode_is ("auto"))
3180 plotboxaspectratio = Matrix (1, 3, 1.0);
3181 else
3182 {
3183 Matrix da = get_dataaspectratio ().matrix_value ();
3184 Matrix pba (1, 3, 0.0);
3185
3186 pba(0) = dx/da(0);
3187 pba(1) = dy/da(1);
3188 pba(2) = dz/da(2);
3189 }
3190 }
3191
3192 // FIXME -- if plotboxaspectratiomode is "manual", limits
3193 // and/or dataaspectratio might be adapted.
3194}
3195
3196// The INTERNAL flag defines whether position or outerposition is used.
3197
3198Matrix
3199axes::properties::get_boundingbox (bool internal) const
3200{
3201 graphics_object obj = gh_manager::get_object (get_parent ());
3202 Matrix parent_bb = obj.get_properties ().get_boundingbox (true);
3203 Matrix pos = (internal ?
3204 get_position ().matrix_value ()
3205 : get_outerposition ().matrix_value ());
3206
3207
3208 pos = convert_position (pos, get_units (), "pixels",
3209 parent_bb.extract_n (0, 2, 1, 2));
3210 pos(0)--;
3211 pos(1)--;
3212 pos(1) = parent_bb(3) - pos(1) - pos(3);
3213
3214 return pos;
3215}
3216
3217ColumnVector
3218graphics_xform::xform_vector (double x, double y, double z)
3219{
3220 return ::xform_vector (x, y, z);
3221}
3222
3223Matrix
3224graphics_xform::xform_eye (void)
3225{
3226 return ::xform_matrix ();
3227}
3228
3229ColumnVector
3230graphics_xform::transform (double x, double y, double z,
3231 bool use_scale) const
3232{
3233 if (use_scale)
3234 {
3235 x = sx.scale (x);
3236 y = sy.scale (y);
3237 z = sz.scale (z);
3238 }
3239
3240 return ::transform (xform, x, y, z);
3241}
3242
3243ColumnVector
3244graphics_xform::untransform (double x, double y, double z,
3245 bool use_scale) const
3246{
3247 ColumnVector v = ::transform (xform_inv, x, y, z);
3248
3249 if (use_scale)
3250 {
3251 v(0) = sx.unscale (v(0));
3252 v(1) = sy.unscale (v(1));
3253 v(2) = sz.unscale (v(2));
3254 }
3255
3256 return v;
3257}
3258
3259octave_value
3260axes::get_default (const caseless_str& name) const
3261{
3262 octave_value retval = default_properties.lookup (name);
3263
3264 if (retval.is_undefined ())
3265 {
3266 graphics_handle parent = get_parent ();
3267 graphics_object parent_obj = gh_manager::get_object (parent);
3268
3269 retval = parent_obj.get_default (name);
3270 }
3271
3272 return retval;
3273}
3274
3275// FIXME -- remove.
3276// FIXME -- maybe this should go into array_property class?
3277/*
3278static void
3279check_limit_vals (double& min_val, double& max_val, double& min_pos,
3280 const array_property& data)
3281{
3282 double val = data.min_val ();
3283 if (! (xisinf (val) || xisnan (val)) && val < min_val)
3284 min_val = val;
3285 val = data.max_val ();
3286 if (! (xisinf (val) || xisnan (val)) && val > max_val)
3287 max_val = val;
3288 val = data.min_pos ();
3289 if (! (xisinf (val) || xisnan (val)) && val > 0 && val < min_pos)
3290 min_pos = val;
3291}
3292*/
3293
3294static void
3295check_limit_vals (double& min_val, double& max_val, double& min_pos,
3296 const octave_value& data)
3297{
3298 if (data.is_matrix_type ())
3299 {
3300 Matrix m = data.matrix_value ();
3301
3302 if (! error_state && m.numel () == 3)
3303 {
3304 double val;
3305
3306 val = m(0);
3307 if (! (xisinf (val) || xisnan (val)) && val < min_val)
3308 min_val = val;
3309
3310 val = m(1);
3311 if (! (xisinf (val) || xisnan (val)) && val > max_val)
3312 max_val = val;
3313
3314 val = m(2);
3315 if (! (xisinf (val) || xisnan (val)) && val > 0 && val < min_pos)
3316 min_pos = val;
3317 }
3318 }
3319}
3320
3321// magform(x) Returns (a, b), where x = a * 10^b, a >= 1., and b is
3322// integral.
3323
3324static void
3325magform (double x, double& a, int& b)
3326{
3327 if (x == 0)
3328 {
3329 a = 0;
3330 b = 0;
3331 }
3332 else
3333 {
3334 double l = std::log10 (std::abs (x));
3335 double r = std::fmod (l, 1.);
3336 a = std::pow (10.0, r);
3337 b = static_cast<int> (l-r);
3338 if (a < 1)
3339 {
3340 a *= 10;
3341 b -= 1;
3342 }
3343
3344 if (x < 0)
3345 a = -a;
3346 }
3347}
3348
3349// A translation from Tom Holoryd's python code at
3350// http://kurage.nimh.nih.gov/tomh/tics.py
3351// FIXME -- add log ticks
3352
3353double
3354axes::properties::calc_tick_sep (double lo, double hi)
3355{
3356 int ticint = 5;
3357
3358 // Reference: Lewart, C. R., "Algorithms SCALE1, SCALE2, and
3359 // SCALE3 for Determination of Scales on Computer Generated
3360 // Plots", Communications of the ACM, 10 (1973), 639-640.
3361 // Also cited as ACM Algorithm 463.
3362
3363 double a;
3364 int b, x;
3365
3366 magform ((hi-lo)/ticint, a, b);
3367
3368 static const double sqrt_2 = sqrt (2.0);
3369 static const double sqrt_10 = sqrt (10.0);
3370 static const double sqrt_50 = sqrt (50.0);
3371
3372 if (a < sqrt_2)
3373 x = 1;
3374 else if (a < sqrt_10)
3375 x = 2;
3376 else if (a < sqrt_50)
3377 x = 5;
3378 else
3379 x = 10;
3380
3381 return x * std::pow (10., b);
3382
3383}
3384
3385// Attempt to make "nice" limits from the actual max and min of the
3386// data. For log plots, we will also use the smallest strictly positive
3387// value.
3388
3389Matrix
3390axes::properties::get_axis_limits (double xmin, double xmax,
3391 double min_pos, bool logscale)
3392{
3393 Matrix retval;
3394
3395 double min_val = xmin;
3396 double max_val = xmax;
3397
3398 if (! (xisinf (min_val) || xisinf (max_val)))
3399 {
3400 if (logscale)
3401 {
3402 if (xisinf (min_pos))
3403 {
3404 // warning ("axis: logscale with no positive values to plot");
3405 return retval;
3406 }
3407
3408 if (min_val <= 0)
3409 {
3410 warning ("axis: omitting nonpositive data in log plot");
3411 min_val = min_pos;
3412 }
3413 // FIXME -- maybe this test should also be relative?
3414 if (std::abs (min_val - max_val) < sqrt (DBL_EPSILON))
3415 {
3416 min_val *= 0.9;
3417 max_val *= 1.1;
3418 }
3419 min_val = pow (10, floor (log10 (min_val)));
3420 max_val = pow (10, ceil (log10 (max_val)));
3421 }
3422 else
3423 {
3424 if (min_val == 0 && max_val == 0)
3425 {
3426 min_val = -1;
3427 max_val = 1;
3428 }
3429 // FIXME -- maybe this test should also be relative?
3430 else if (std::abs (min_val - max_val) < sqrt (DBL_EPSILON))
3431 {
3432 min_val -= 0.1 * std::abs (min_val);
3433 max_val += 0.1 * std::abs (max_val);
3434 }
3435
3436 double tick_sep = calc_tick_sep (min_val , max_val);
3437 min_val = tick_sep * std::floor (min_val / tick_sep);
3438 max_val = tick_sep * ceil (max_val / tick_sep);
3439 }
3440 }
3441
3442 retval.resize (1, 2);
3443
3444 retval(0) = min_val;
3445 retval(1) = max_val;
3446
3447 return retval;
3448}
3449
3450void
3451axes::properties::calc_ticks_and_lims (array_property& lims,
3452 array_property& ticks,
3453 bool limmode_is_auto, bool is_logscale)
3454{
3455 // FIXME -- add log ticks and lims
3456
3457 if (lims.get ().is_empty ())
3458 return;
3459
3460 double lo = (lims.get ().matrix_value ()) (0);
3461 double hi = (lims.get ().matrix_value ()) (1);
3462 // FIXME should this be checked for somewhere else? (i.e. set{x,y,z}lim)
3463 if (hi < lo)
3464 {
3465 double tmp = hi;
3466 hi = lo;
3467 lo = tmp;
3468 }
3469
3470 if (is_logscale)
3471 {
3472 // FIXME we should check for negtives here
3473 hi = std::log10 (hi);
3474 lo = std::log10 (lo);
3475 }
3476
3477 double tick_sep = calc_tick_sep (lo , hi);
3478
3479 int i1 = static_cast<int> (std::floor (lo / tick_sep));
3480 int i2 = static_cast<int> (std::ceil (hi / tick_sep));
3481
3482 if (limmode_is_auto)
3483 {
3484 // adjust limits to include min and max tics
3485 Matrix tmp_lims (1,2);
3486 tmp_lims(0) = tick_sep * i1;
3487 tmp_lims(1) = tick_sep * i2;
3488
3489 if (is_logscale)