3Copyright (C) 2007, 2008, 2009 John W. Eaton
5This file is part of Octave.
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.
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
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/>.
50#include "ov-fcn-handle.h"
53#include "unwind-prot.h"
56static octave_value xget (const graphics_handle& h, const caseless_str& name);
59gripe_set_invalid (const std::string& pname)
61 error ("set: invalid value for %s property", pname.c_str ());
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.
69validate_property_name (const std::string& who, const std::string& what,
70 const std::set<std::string>& pnames,
71 const caseless_str& pname)
73 size_t len = pname.length ();
74 std::set<std::string> matches;
76 for (std::set<std::string>::const_iterator p = pnames.begin ();
77 p != pnames.end (); p++)
79 if (pname.compare (*p, len))
81 if (len == p->length ())
91 size_t num_matches = matches.size ();
95 error ("%s: unknown %s property %s",
96 who.c_str (), what.c_str (), pname.c_str ());
98 else if (num_matches > 1)
100 string_vector sv (matches);
102 std::ostringstream os;
104 sv.list_in_columns (os);
106 std::string match_list = os.str ();
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 ());
111 else if (num_matches == 1)
113 // Exact match was handled above.
115 std::string possible_match = *(matches.begin ());
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 ());
122 return possible_match;
125 return caseless_str ();
131 Matrix cmap (64, 3, 0.0);
133 for (octave_idx_type i = 0; i < 64; i++)
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
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)
148 else if (x >= 7.0/8.0)
149 cmap(i,0) = -4.0 * x + 9.0/2.0;
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)
155 else if (x >= 5.0/8.0 && x < 7.0/8.0)
156 cmap(i,1) = -4.0 * x + 7.0/2.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)
162 else if (x >= 3.0/8.0 && x < 5.0/8.0)
163 cmap(i,2) = -4.0 * x + 5.0/2.0;
170default_screendepth (void)
172 return display_info::depth ();
176default_screensize (void)
178 Matrix retval (1, 4, 1.0);
180 retval(2) = display_info::width ();
181 retval(3) = display_info::height ();
187default_screenpixelsperinch (void)
189 return (display_info::x_dpi () + display_info::y_dpi ()) / 2;
193default_colororder (void)
195 Matrix retval (7, 3, 0.0);
230 Matrix retval (1, 2);
239default_axes_position (void)
241 Matrix m (1, 4, 0.0);
250default_axes_outerposition (void)
252 Matrix m (1, 4, 0.0);
258default_axes_tick (void)
260 Matrix m (1, 6, 0.0);
271default_axes_ticklength (void)
273 Matrix m (1, 2, 0.01);
279default_figure_position (void)
281 Matrix m (1, 4, 0.0);
290default_figure_papersize (void)
292 Matrix m (1, 2, 0.0);
299default_figure_paperposition (void)
301 Matrix m (1, 4, 0.0);
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))
314 Matrix retval (1, 4);
317 if (from_units.compare ("pixels"))
319 else if (from_units.compare ("normalized"))
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);
326 else if (from_units.compare ("characters"))
329 res = xget (0, "screenpixelsperinch").double_value ();
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;
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;
348 res = xget (0, "screenpixelsperinch").double_value ();
352 if (from_units.compare ("points"))
354 else if (from_units.compare ("inches"))
356 else if (from_units.compare ("centimeters"))
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;
368 if (! to_units.compare ("pixels"))
370 if (to_units.compare ("normalized"))
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);
377 else if (to_units.compare ("characters"))
380 res = xget (0, "screenpixelsperinch").double_value ();
384 f = 12.0 * res / 74.951;
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;
397 res = xget (0, "screenpixelsperinch").double_value ();
401 if (to_units.compare ("points"))
403 else if (to_units.compare ("inches"))
405 else if (to_units.compare ("centimeters"))
410 retval(0) = (retval(0) - 1) / f;
411 retval(1) = (retval(1) - 1) / f;
421static graphics_object
422xget_ancestor (const graphics_object& go_arg, const std::string& type)
424 graphics_object go = go_arg;
428 if (go.valid_object ())
433 go = gh_manager::get_object (go.get_parent ());
436 return graphics_object ();
442convert_cdata (const base_properties& props, const octave_value& cdata,
443 bool is_scaled, int cdim)
445 dim_vector dv (cdata.dims ());
447 if (dv.length () == cdim && dv(cdim-1) == 3)
450 Matrix cmap (1, 3, 0.0);
451 Matrix clim (1, 2, 0.0);
453 graphics_object go = gh_manager::get_object (props.get___myhandle__ ());
454 graphics_object fig = xget_ancestor (go, "figure");
456 if (fig.valid_object ())
458 Matrix _cmap = fig.get (caseless_str ("colormap")).matrix_value ();
466 graphics_object ax = xget_ancestor (go, "axes");
468 if (ax.valid_object ())
470 Matrix _clim = ax.get (caseless_str ("clim")).matrix_value ();
482 octave_idx_type lda = a.numel () / static_cast<octave_idx_type> (3);
483 octave_idx_type nc = cmap.rows ();
485 double *av = a.fortran_vec ();
486 const double *cmapv = cmap.data ();
487 const double *cv = 0;
488 const octave_uint8 *icv = 0;
490 if (cdata.is_integer_type ())
491 icv = cdata.uint8_array_value ().data ();
493 cv = cdata.array_value ().data ();
495 for (octave_idx_type i = 0; i < lda; i++)
497 double x = (cv ? cv[i] : double (icv[i]));
500 x = xround ((nc - 1) * (x - clim(0)) / (clim(1) - clim(0)));
517 octave_idx_type idx = static_cast<octave_idx_type> (x);
520 av[i+lda] = cmapv[idx+nc];
521 av[i+2*lda] = cmapv[idx+2*nc];
525 return octave_value (a);
530get_array_limits (const Array<T>& m, double& emin, double& emax,
533 const T *data = m.data ();
534 octave_idx_type n = m.numel ();
536 for (octave_idx_type i = 0; i < n; i++)
538 double e = double (data[i]);
540 if (! (xisinf (e) || xisnan (e)))
548 if (e > 0 && e < eminp)
555lookup_object_name (const caseless_str& name, caseless_str& go_name,
558 int len = name.length ();
564 caseless_str pfx = name.substr (0, 4);
566 if (pfx.compare ("axes") || pfx.compare ("line")
567 || pfx.compare ("text"))
571 pfx = name.substr (0, 5);
573 if (pfx.compare ("image") || pfx.compare ("patch"))
577 pfx = name.substr (0, 6);
579 if (pfx.compare ("figure"))
583 pfx = name.substr (0, 7);
585 if (pfx.compare ("surface") || pfx.compare ("hggroup"))
594 rest = name.substr (offset);
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 ())
607 base_graphics_object *go = 0;
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);
629// ---------------------------------------------------------------------
632base_property::set (const octave_value& v, bool do_run )
640 graphics_object go = gh_manager::get_object (parent);
643 graphics_backend backend = go.get_backend();
645 backend.property_changed (go, id);
650 if (do_run && ! error_state)
651 run_listeners (POSTSET);
661base_property::run_listeners (listener_mode mode)
663 const octave_value_list& l = listeners[mode];
665 for (int i = 0; i < l.length (); i++)
667 gh_manager::execute_callback (parent, l(i), octave_value ());
674radio_values::radio_values (const std::string& opt_string)
677 size_t len = opt_string.length ();
678 bool done = len == 0;
682 size_t end = opt_string.find ('|', beg);
684 if (end == std::string::npos)
690 std::string t = opt_string.substr (beg, end-beg);
692 // Might want more error checking here...
695 t = t.substr (1, t.length () - 2);
698 else if (beg == 0) // ensure default value
701 possible_vals.insert (t);
708color_values::str2rgb (std::string str)
710 double tmp_rgb[3] = {0, 0, 0};
712 unsigned int len = str.length();
714 std::transform (str.begin (), str.end (), str.begin (), tolower);
716 if (str.compare(0, len, "blue", 0, len) == 0)
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)
723 else if (str.compare(0, len, "green", 0, len) == 0)
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;
739 for (int i = 0; i < 3; i++)
740 xrgb(i) = tmp_rgb[i];
747color_property::do_set (const octave_value& val)
749 if (val.is_string ())
751 std::string s = val.string_value ();
755 if (radio_val.contains (s))
757 if (current_type != radio_t || current_val != s)
760 current_type = radio_t;
766 color_values col (s);
769 if (current_type != color_t || col != color_val)
772 current_type = color_t;
777 error ("invalid value for color property \"%s\" (value = %s)",
778 get_name ().c_str (), s.c_str ());
782 error ("invalid value for color property \"%s\"",
783 get_name ().c_str ());
785 else if (val.is_numeric_type ())
787 Matrix m = val.matrix_value ();
791 color_values col (m (0), m (1), m(2));
794 if (current_type != color_t || col != color_val)
797 current_type = color_t;
803 error ("invalid value for color property \"%s\"",
804 get_name ().c_str ());
807 error ("invalid value for color property \"%s\"",
808 get_name ().c_str ());
814double_radio_property::do_set (const octave_value& val)
816 if (val.is_string ())
818 std::string s = val.string_value ();
820 if (! s.empty () && radio_val.contains (s))
822 if (current_type != radio_t || s != current_val)
825 current_type = radio_t;
830 error ("invalid value for double_radio property \"%s\"",
831 get_name ().c_str ());
833 else if (val.is_scalar_type () && val.is_real_type ())
835 double new_dval = val.double_value ();
837 if (current_type != double_t || new_dval != dval)
840 current_type = double_t;
845 error ("invalid value for double_radio property \"%s\"",
846 get_name ().c_str ());
852array_property::validate (const octave_value& v)
856 // FIXME -- should we always support []?
857 if (v.is_empty () && v.is_numeric_type ())
861 if (type_constraints.size () > 0)
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 ())
869 xok = v.is_numeric_type ();
873 dim_vector vdims = v.dims ();
874 int vlen = vdims.length ();
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)
883 dim_vector itdims = (*it);
885 if (itdims.length () == vlen)
889 for (int i = 0; xok && i < vlen; i++)
890 if (itdims(i) >= 0 && itdims(i) != vdims(i))
902array_property::is_equal (const octave_value& v) const
904 if (data.type_name () == v.type_name ())
906 if (data.dims () == v.dims ())
909#define CHECK_ARRAY_EQUAL(T,F,A) \
911 if (data.numel () == 1) \
912 return data.F ## scalar_value () == \
913 v.F ## scalar_value (); \
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 ();\
925 for (int i = 0; flag && i < data.numel (); i++) \
926 if (d1[i] != d2[i]) \
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)
960array_property::get_data_limits (void)
962 xmin = xminp = octave_Inf;
965 if (! data.is_empty ())
967 if (data.is_integer_type ())
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);
987 get_array_limits (data.array_value (), xmin, xmax, xminp);
992handle_property::do_set (const octave_value& v)
994 double dv = v.double_value ();
998 graphics_handle gh = gh_manager::lookup (dv);
1000 if (xisnan (gh.value ()) || gh.ok ())
1002 if (current_val != gh)
1009 error ("set: invalid graphics handle (= %g) for property \"%s\"",
1010 dv, get_name ().c_str ());
1013 error ("set: invalid graphics handle for property \"%s\"",
1014 get_name ().c_str ());
1020callback_property::validate (const octave_value& v) const
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
1028 if (v.is_function_handle ())
1030 else if (v.is_string ())
1031 // complete validation will be done at execution-time
1033 else if (v.is_cell () && v.length () > 0
1034 && (v.rows() == 1 || v.columns () == 1)
1035 && v.cell_value ()(0).is_function_handle ())
1037 else if (v.is_empty ())
1044callback_property::execute (const octave_value& data) const
1046 if (callback.is_defined () && ! callback.is_empty ())
1047 gh_manager::execute_callback (get_parent (), callback, data);
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;
1055property::create (const std::string& name, const graphics_handle& h,
1056 const caseless_str& type, const octave_value_list& args)
1060 if (type.compare ("string"))
1062 std::string val = (args.length () > 0 ? args(0).string_value () : "");
1065 retval = property (new string_property (name, h, val));
1067 else if (type.compare ("any"))
1070 (args.length () > 0 ? args(0) : octave_value (Matrix ()));
1072 retval = property (new any_property (name, h, val));
1074 else if (type.compare ("radio"))
1076 if (args.length () > 0)
1078 std::string vals = args(0).string_value ();
1082 retval = property (new radio_property (name, h, vals));
1084 if (args.length () > 1)
1085 retval.set (args(1));
1088 error ("addproperty: invalid argument for radio property, expected a string value");
1091 error ("addproperty: missing possible values for radio property");
1093 else if (type.compare ("double"))
1095 double d = (args.length () > 0 ? args(0).double_value () : 0);
1098 retval = property (new double_property (name, h, d));
1100 else if (type.compare ("handle"))
1102 double hh = (args.length () > 0 ? args(0).double_value () : octave_NaN);
1106 graphics_handle gh (hh);
1108 retval = property (new handle_property (name, h, gh));
1111 else if (type.compare ("boolean"))
1113 retval = property (new bool_property (name, h, false));
1115 if (args.length () > 0)
1116 retval.set (args(0));
1118 else if (type.compare ("data"))
1120 retval = property (new array_property (name, h, Matrix ()));
1122 if (args.length () > 0)
1124 retval.set (args(0));
1126 // FIXME -- additional argument could define constraints,
1127 // but is this really useful?
1130 else if (type.compare ("color"))
1132 color_values cv (0, 0, 0);
1135 if (args.length () > 1)
1136 rv = radio_values (args(1).string_value ());
1140 retval = property (new color_property (name, h, cv, rv));
1144 if (args.length () > 0 && ! args(0).is_empty ())
1145 retval.set (args(0));
1147 retval.set (rv.default_value ());
1153 caseless_str go_name, go_rest;
1155 if (lookup_object_name (type, go_name, go_rest))
1159 std::map<caseless_str, graphics_object>::const_iterator it =
1160 dprop_obj_map.find (go_name);
1162 if (it == dprop_obj_map.end ())
1164 base_graphics_object *bgo =
1165 make_graphics_object_from_type (go_name);
1169 go = graphics_object (bgo);
1171 dprop_obj_map[go_name] = go;
1177 if (go.valid_object ())
1179 property prop = go.get_properties ().get_property (go_rest);
1183 retval = prop.clone ();
1185 retval.set_parent (h);
1186 retval.set_name (name);
1188 if (args.length () > 0)
1189 retval.set (args(0));
1193 error ("addproperty: invalid object type (= %s)",
1197 error ("addproperty: unsupported type for dynamic property (= %s)",
1204// ---------------------------------------------------------------------
1207property_list::set (const caseless_str& name, const octave_value& val)
1211 size_t len = name.length ();
1215 caseless_str pfx = name.substr (0, 4);
1217 if (pfx.compare ("axes") || pfx.compare ("line")
1218 || pfx.compare ("text"))
1222 pfx = name.substr (0, 5);
1224 if (pfx.compare ("image") || pfx.compare ("patch"))
1228 pfx = name.substr (0, 6);
1230 if (pfx.compare ("figure"))
1234 pfx = name.substr (0, 7);
1236 if (pfx.compare ("surface") || pfx.compare ("hggroup"))
1244 // FIXME -- should we validate property names and values here?
1246 std::string pname = name.substr (offset);
1248 std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
1249 std::transform (pname.begin (), pname.end (), pname.begin (), tolower);
1251 bool has_property = false;
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);
1271 bool remove = false;
1272 if (val.is_string ())
1274 caseless_str tval = val.string_value ();
1276 remove = tval.compare ("remove");
1279 pval_map_type& pval_map = plist_map[pfx];
1283 pval_map_iterator p = pval_map.find (pname);
1285 if (p != pval_map.end ())
1289 pval_map[pname] = val;
1292 error ("invalid %s property `%s'", pfx.c_str (), pname.c_str ());
1296 if (! error_state && offset == 0)
1297 error ("invalid default property specification");
1301property_list::lookup (const caseless_str& name) const
1303 octave_value retval;
1307 size_t len = name.length ();
1311 caseless_str pfx = name.substr (0, 4);
1313 if (pfx.compare ("axes") || pfx.compare ("line")
1314 || pfx.compare ("text"))
1318 pfx = name.substr (0, 5);
1320 if (pfx.compare ("image") || pfx.compare ("patch"))
1324 pfx = name.substr (0, 6);
1326 if (pfx.compare ("figure"))
1330 pfx = name.substr (0, 7);
1332 if (pfx.compare ("surface") || pfx.compare ("hggroup"))
1340 std::string pname = name.substr (offset);
1342 std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
1343 std::transform (pname.begin (), pname.end (), pname.begin (), tolower);
1345 plist_map_const_iterator p = find (pfx);
1349 const pval_map_type& pval_map = p->second;
1351 pval_map_const_iterator q = pval_map.find (pname);
1353 if (q != pval_map.end ())
1363property_list::as_struct (const std::string& prefix_arg) const
1367 for (plist_map_const_iterator p = begin (); p != end (); p++)
1369 std::string prefix = prefix_arg + p->first;
1371 const pval_map_type pval_map = p->second;
1373 for (pval_map_const_iterator q = pval_map.begin ();
1374 q != pval_map.end ();
1376 m.assign (prefix + q->first, q->second);
1382graphics_handle::graphics_handle (const octave_value& a)
1389 double tval = a.double_value ();
1394 error ("invalid graphics handle");
1399graphics_object::set (const octave_value_list& args)
1401 int nargin = args.length ();
1405 else if (nargin % 2 == 0)
1407 for (int i = 0; i < nargin; i += 2)
1409 caseless_str name = args(i).string_value ();
1413 octave_value val = args(i+1);
1415 if (val.is_string ())
1417 caseless_str tval = val.string_value ();
1419 if (tval.compare ("default"))
1420 val = get_default (name);
1421 else if (tval.compare ("factory"))
1422 val = get_factory_default (name);
1428 rep->set (name, val);
1431 error ("set: expecting argument %d to be a property name", i);
1435 error ("set: invalid number of arguments");
1439make_handle_fraction (void)
1441 static double maxrand = RAND_MAX + 2.0;
1443 return (rand () + 1.0) / maxrand;
1447gh_manager::get_handle (const std::string& go_name)
1449 graphics_handle retval;
1451 if (go_name == "figure")
1453 // Figure handles are positive integers corresponding to the
1456 // We always want the lowest unused figure number.
1460 while (handle_map.find (retval) != handle_map.end ())
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
1470 free_list_iterator p = handle_free_list.begin ();
1472 if (p != handle_free_list.end ())
1475 handle_free_list.erase (p);
1479 retval = graphics_handle (next_handle);
1481 next_handle = ceil (next_handle) - 1.0 - make_handle_fraction ();
1489gh_manager::do_free (const graphics_handle& h)
1493 if (h.value () != 0)
1495 iterator p = handle_map.find (h);
1497 if (p != handle_map.end ())
1499 base_properties& bp = p->second.get_properties ();
1501 bp.set_beingdeleted (true);
1503 bp.delete_children ();
1505 octave_value val = bp.get_deletefcn ();
1507 bp.execute_deletefcn ();
1510 graphics_backend backend = p->second.get_backend ();
1512 backend.object_destroyed (p->second);
1514 // Note: this will be valid only for first explicitly
1515 // deleted object. All its children will then have an
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.
1523 handle_map.erase (p);
1526 handle_free_list.insert (ceil (h.value ()) - make_handle_fraction ());
1529 error ("graphics_handle::free: invalid object %g", h.value ());
1532 error ("graphics_handle::free: can't delete root figure");
1536gh_manager *gh_manager::instance = 0;
1539xset (const graphics_handle& h, const caseless_str& name,
1540 const octave_value& val)
1542 graphics_object obj = gh_manager::get_object (h);
1543 obj.set (name, val);
1547xset (const graphics_handle& h, const octave_value_list& args)
1549 if (args.length () > 0)
1551 graphics_object obj = gh_manager::get_object (h);
1558xget (const graphics_handle& h, const caseless_str& name)
1560 graphics_object obj = gh_manager::get_object (h);
1561 return obj.get (name);
1564static graphics_handle
1565reparent (const octave_value& ov, const std::string& who,
1566 const std::string& property, const graphics_handle& new_parent,
1569 graphics_handle h = octave_NaN;
1571 double val = ov.double_value ();
1575 h = gh_manager::lookup (val);
1579 graphics_object obj = gh_manager::get_object (h);
1581 graphics_handle parent_h = obj.get_parent ();
1583 graphics_object parent_obj = gh_manager::get_object (parent_h);
1585 parent_obj.remove_child (h);
1588 obj.set ("parent", new_parent.value ());
1590 obj.reparent (new_parent);
1593 error ("%s: invalid graphics handle (= %g) for %s",
1594 who.c_str (), val, property.c_str ());
1597 error ("%s: expecting %s to be a graphics handle",
1598 who.c_str (), property.c_str ());
1603// This function is NOT equivalent to the scripting language function gcf.
1607 octave_value val = xget (0, "currentfigure");
1609 return val.is_empty () ? octave_NaN : val.double_value ();
1612// This function is NOT equivalent to the scripting language function gca.
1616 octave_value val = xget (gcf (), "currentaxes");
1618 return val.is_empty () ? octave_NaN : val.double_value ();
1622adopt (const graphics_handle& p, const graphics_handle& h)
1624 graphics_object parent_obj = gh_manager::get_object (p);
1626 parent_obj.adopt (h);
1630is_handle (const graphics_handle& h)
1636is_handle (double val)
1638 graphics_handle h = gh_manager::lookup (val);
1644is_handle (const octave_value& val)
1646 octave_value retval = false;
1648 if (val.is_real_scalar () && is_handle (val.double_value ()))
1650 else if (val.is_real_matrix ())
1652 if (val.is_string ())
1653 retval = boolNDArray (val.dims (), false);
1656 const NDArray handles = val.array_value ();
1660 boolNDArray result (handles.dims ());
1662 for (octave_idx_type i = 0; i < handles.numel (); i++)
1663 result.xelem (i) = is_handle (handles (i));
1674is_figure (double val)
1676 graphics_object obj = gh_manager::get_object (val);
1678 return obj && obj.isa ("figure");
1682xcreatefcn (const graphics_handle& h)
1684 graphics_object obj = gh_manager::get_object (h);
1685 obj.get_properties ().execute_createfcn ();
1688// ---------------------------------------------------------------------
1691base_graphics_backend::property_changed (const graphics_handle& h, int id)
1693 graphics_object go = gh_manager::get_object (h);
1695 property_changed (go, id);
1699base_graphics_backend::object_created (const graphics_handle& h)
1701 graphics_object go = gh_manager::get_object (h);
1703 object_created (go);
1707base_graphics_backend::object_destroyed (const graphics_handle& h)
1709 graphics_object go = gh_manager::get_object (h);
1711 object_destroyed (go);
1713// ---------------------------------------------------------------------
1716maybe_set_children (const Matrix& kids, const octave_value& val)
1718 const Matrix new_kids = val.matrix_value ();
1724 if (kids.numel () == new_kids.numel ())
1727 Matrix t2 = new_kids;
1739 error ("set: new children must be a permutation of existing children");
1744 error ("set: expecting children to be array of graphics handles");
1747 return ok ? new_kids : kids;
1751base_properties::set_from_list (base_graphics_object& obj,
1752 property_list& defaults)
1754 std::string go_name = graphics_object_name ();
1756 property_list::plist_map_const_iterator p = defaults.find (go_name);
1758 if (p != defaults.end ())
1760 const property_list::pval_map_type pval_map = p->second;
1762 for (property_list::pval_map_const_iterator q = pval_map.begin ();
1763 q != pval_map.end ();
1766 std::string pname = q->first;
1768 obj.set (pname, q->second);
1772 error ("error setting default property %s", pname.c_str ());
1780base_properties::get_dynamic (const caseless_str& name) const
1782 octave_value retval;
1784 std::map<caseless_str, property, cmp_caseless_str>::const_iterator it = all_props.find (name);
1786 if (it != all_props.end ())
1787 retval = it->second.get ();
1789 error ("get: unknown property \"%s\"", name.c_str ());
1795base_properties::get_dynamic (bool all) const
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 ());
1807std::map<std::string, std::set<std::string> > base_properties::all_dynamic_properties;
1809std::set<std::string>
1810base_properties::dynamic_property_names (const std::string& cname)
1812 return all_dynamic_properties[cname];
1816base_properties::has_dynamic_property (const std::string& pname,
1817 const std::string& cname)
1819 const std::set<std::string>& dynprops = dynamic_property_names (cname);
1821 return dynprops.find (pname) != dynprops.end ();
1825base_properties::set_dynamic (const caseless_str& pname,
1826 const std::string& cname,
1827 const octave_value& val)
1829 std::map<caseless_str, property, cmp_caseless_str>::iterator it = all_props.find (pname);
1831 if (it != all_props.end ())
1832 it->second.set (val);
1834 error ("set: unknown property \"%s\"", pname.c_str ());
1838 all_dynamic_properties[cname].insert (pname);
1845base_properties::get_property_dynamic (const caseless_str& name)
1847 std::map<caseless_str, property, cmp_caseless_str>::const_iterator it = all_props.find (name);
1849 if (it == all_props.end ())
1851 error ("get_property: unknown property \"%s\"", name.c_str ());
1859base_properties::remove_child (const graphics_handle& h)
1861 octave_idx_type k = -1;
1862 octave_idx_type n = children.numel ();
1863 for (octave_idx_type i = 0; i < n; i++)
1865 if (h.value () == children(i))
1874 Matrix new_kids (n-1, 1);
1875 octave_idx_type j = 0;
1876 for (octave_idx_type i = 0; i < n; i++)
1879 new_kids(j++) = children(i);
1881 children = new_kids;
1887base_properties::set_parent (const octave_value& val)
1889 double tmp = val.double_value ();
1891 graphics_handle new_parent = octave_NaN;
1895 new_parent = gh_manager::lookup (tmp);
1897 if (new_parent.ok ())
1899 graphics_object parent_obj = gh_manager::get_object (get_parent ());
1901 parent_obj.remove_child (__myhandle__);
1903 parent = new_parent.as_octave_value ();
1905 ::adopt (parent.handle_value (), __myhandle__);
1908 error ("set: invalid graphics handle (= %g) for parent", tmp);
1911 error ("set: expecting parent to be a graphics handle");
1915base_properties::set_children (const octave_value& val)
1917 children = maybe_set_children (children, val);
1921base_properties::mark_modified (void)
1923 __modified__ = "on";
1924 graphics_object parent_obj = gh_manager::get_object (get_parent ());
1926 parent_obj.mark_modified ();
1930base_properties::override_defaults (base_graphics_object& obj)
1932 graphics_object parent_obj = gh_manager::get_object (get_parent ());
1935 parent_obj.override_defaults (obj);
1939base_properties::update_axis_limits (const std::string& axis_type) const
1941 graphics_object obj = gh_manager::get_object (__myhandle__);
1944 obj.update_axis_limits (axis_type);
1948base_properties::delete_children (void)
1950 octave_idx_type n = children.numel ();
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++)
1956 graphics_object go = gh_manager::get_object (children(i));
1958 if (go.valid_object ())
1959 gh_manager::free (children(i));
1964base_properties::get_backend (void) const
1966 graphics_object go = gh_manager::get_object (get_parent ());
1969 return go.get_backend ();
1971 return graphics_backend ();
1975base_properties::update_boundingbox (void)
1977 Matrix kids = get_children ();
1979 for (int i = 0; i < kids.numel (); i++)
1981 graphics_object go = gh_manager::get_object (kids(i));
1983 if (go.valid_object ())
1984 go.get_properties ().update_boundingbox ();
1989base_properties::add_listener (const caseless_str& nm, const octave_value& v,
1992 property p = get_property (nm);
1994 if (! error_state && p.ok ())
1995 p.add_listener (v, mode);
1999base_properties::delete_listener (const caseless_str& nm,
2000 const octave_value& v, listener_mode mode)
2002 property p = get_property (nm);
2004 if (! error_state && p.ok ())
2005 p.delete_listener (v, mode);
2008// ---------------------------------------------------------------------
2010class gnuplot_backend : public base_graphics_backend
2013 gnuplot_backend (void)
2014 : base_graphics_backend ("gnuplot") { }
2016 ~gnuplot_backend (void) { }
2018 bool is_valid (void) const { return true; }
2020 void object_destroyed (const graphics_object& go)
2022 if (go.isa ("figure"))
2024 const figure::properties& props =
2025 dynamic_cast<const figure::properties&> (go.get_properties ());
2027 send_quit (props.get___plot_stream__ ());
2031 void property_changed (const graphics_object& go, int id)
2033 if (go.isa ("figure"))
2035 graphics_object obj (go);
2037 figure::properties& props =
2038 dynamic_cast<figure::properties&> (obj.get_properties ());
2042 case base_properties::VISIBLE:
2043 if (! props.is_visible ())
2045 send_quit (props.get___plot_stream__ ());
2046 props.set___plot_stream__ (Matrix ());
2047 props.set___enhanced__ (false);
2054 void redraw_figure (const graphics_object& go) const
2056 octave_value_list args;
2057 args(0) = go.get_handle ().as_octave_value ();
2058 feval ("gnuplot_drawnow", args);
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
2065 octave_value_list args;
2066 if (! debug_file.empty ())
2067 args(4) = debug_file;
2071 args(0) = go.get_handle ().as_octave_value ();
2072 feval ("gnuplot_drawnow", args);
2075 Matrix get_canvas_size (const graphics_handle&) const
2077 Matrix sz (1, 2, 0.0);
2081 double get_screen_resolution (void) const
2084 Matrix get_screen_size (void) const
2085 { return Matrix (1, 2, 0.0); }
2088 void send_quit (const octave_value& pstream) const
2090 if (! pstream.is_empty ())
2092 octave_value_list args;
2093 Matrix fids = pstream.matrix_value ();
2097 args(1) = "\nquit;\n";
2099 feval ("fputs", args);
2102 feval ("fflush", args);
2103 feval ("pclose", args);
2105 if (fids.numel () > 1)
2108 feval ("pclose", args);
2110 if (fids.numel () > 2)
2113 feval ("waitpid", args);
2122graphics_backend::default_backend (void)
2124 if (available_backends.size () == 0)
2125 register_backend (new gnuplot_backend ());
2127 return available_backends["gnuplot"];
2130std::map<std::string, graphics_backend> graphics_backend::available_backends;
2132// ---------------------------------------------------------------------
2135base_graphics_object::update_axis_limits (const std::string& axis_type)
2137 if (valid_object ())
2139 graphics_object parent_obj = gh_manager::get_object (get_parent ());
2142 parent_obj.update_axis_limits (axis_type);
2145 error ("base_graphics_object::update_axis_limits: invalid graphics object");
2149base_graphics_object::remove_all_listeners (void)
2151 Octave_map m = get (true).map_value ();
2153 for (Octave_map::const_iterator pa = m.begin (); pa != m.end (); pa++)
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?
2159 unwind_protect::frame_id_t uwp_frame = unwind_protect::begin_frame ();
2161 unwind_protect::protect_var (discard_error_messages);
2162 unwind_protect::protect_var (error_state);
2164 discard_error_messages = true;
2166 property p = get_properties ().get_property (pa->first);
2168 if (! error_state && p.ok ())
2169 p.delete_listener ();
2171 unwind_protect::run_frame (uwp_frame);
2175// ---------------------------------------------------------------------
2177#include "graphics-props.cc"
2179// ---------------------------------------------------------------------
2182root_figure::properties::set_currentfigure (const octave_value& v)
2184 graphics_handle val (v);
2189 if (xisnan (val.value ()) || is_handle (val))
2191 currentfigure = val;
2193 gh_manager::push_figure (val);
2196 gripe_set_invalid ("currentfigure");
2200root_figure::properties::set_callbackobject (const octave_value& v)
2202 graphics_handle val (v);
2207 if (xisnan (val.value ()))
2209 if (! cbo_stack.empty ())
2211 val = cbo_stack.front ();
2213 cbo_stack.pop_front ();
2216 callbackobject = val;
2218 else if (is_handle (val))
2220 if (get_callbackobject ().ok ())
2221 cbo_stack.push_front (get_callbackobject ());
2223 callbackobject = val;
2226 gripe_set_invalid ("callbackobject");
2230root_figure::properties::update_units (void)
2232 caseless_str xunits = get_units ();
2234 Matrix ss = default_screensize ();
2236 double dpi = get_screenpixelsperinch ();
2238 if (xunits.compare ("inches"))
2245 else if (xunits.compare ("centimeters"))
2249 ss(2) *= 2.54 / dpi;
2250 ss(3) *= 2.54 / dpi;
2252 else if (xunits.compare ("normalized"))
2254 ss = Matrix (1, 4, 1.0);
2256 else if (xunits.compare ("points"))
2264 set_screensize (ss);
2268root_figure::properties::remove_child (const graphics_handle& gh)
2270 gh_manager::pop_figure (gh);
2272 graphics_handle cf = gh_manager::current_figure ();
2274 xset (0, "currentfigure", cf.value ());
2276 base_properties::remove_child (gh);
2280root_figure::factory_properties = root_figure::init_factory_properties ();
2282// ---------------------------------------------------------------------
2285figure::properties::set_currentaxes (const octave_value& v)
2287 graphics_handle val (v);
2292 if (xisnan (val.value ()) || is_handle (val))
2295 gripe_set_invalid ("currentaxes");
2299figure::properties::remove_child (const graphics_handle& gh)
2301 base_properties::remove_child (gh);
2303 if (gh == currentaxes.handle_value ())
2305 graphics_handle new_currentaxes;
2307 for (octave_idx_type i = 0; i < children.numel (); i++)
2309 graphics_handle kid = children(i);
2311 graphics_object go = gh_manager::get_object (kid);
2313 if (go.isa ("axes"))
2315 new_currentaxes = kid;
2320 currentaxes = new_currentaxes;
2325figure::properties::set_visible (const octave_value& val)
2327 std::string s = val.string_value ();
2332 xset (0, "currentfigure", __myhandle__.value ());
2339figure::properties::get_boundingbox (bool) const
2341 Matrix screen_size = xget (0, "screensize").matrix_value ().extract_n (0, 2, 1, 2);
2344 pos = convert_position (get_position ().matrix_value (), get_units (),
2345 "pixels", screen_size);
2349 pos(1) = screen_size(1) - pos(1) - pos(3);
2355figure::properties::set_boundingbox (const Matrix& bb)
2357 Matrix screen_size = xget (0, "screensize").matrix_value ().extract_n (0, 2, 1, 2);
2360 pos(1) = screen_size(1) - pos(1) - pos(3);
2363 pos = convert_position (pos, "pixels", get_units (), screen_size);
2369figure::properties::set_position (const octave_value& v)
2373 Matrix old_bb, new_bb;
2375 old_bb = get_boundingbox ();
2377 new_bb = get_boundingbox ();
2379 if (old_bb != new_bb)
2381 if (old_bb(2) != new_bb(2) || old_bb(3) != new_bb(3))
2383 execute_resizefcn ();
2384 update_boundingbox ();
2393figure::properties::get_title (void) const
2395 if (is_numbertitle ())
2397 std::ostringstream os;
2398 std::string nm = get_name ();
2400 os << "Figure " << __myhandle__.value ();
2402 os << ": " << get_name ();
2411figure::get_default (const caseless_str& name) const
2413 octave_value retval = default_properties.lookup (name);
2415 if (retval.is_undefined ())
2417 graphics_handle parent = get_parent ();
2418 graphics_object parent_obj = gh_manager::get_object (parent);
2420 retval = parent_obj.get_default (name);
2426// ---------------------------------------------------------------------
2429axes::properties::init (void)
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);
2448 view.add_constraint (dim_vector (1, 2));
2449 cameraposition.add_constraint (dim_vector (1, 3));
2450 Matrix upv (1, 3, 0.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));
2458 x_zlim.resize (1, 2);
2464 calc_ticklabels (xtick, xticklabel, xscale.is ("log"));
2465 calc_ticklabels (ytick, yticklabel, yscale.is ("log"));
2466 calc_ticklabels (ztick, zticklabel, zscale.is ("log"));
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");
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");
2478 xset (xlabel.handle_value (), "verticalalignment", "cap");
2479 xset (ylabel.handle_value (), "verticalalignment", "bottom");
2480 xset (title.handle_value (), "verticalalignment", "bottom");
2482 xset (ylabel.handle_value (), "rotation", 90.0);
2483 xset (zlabel.handle_value (), "visible", "off");
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");
2490 adopt (xlabel.handle_value ());
2491 adopt (ylabel.handle_value ());
2492 adopt (zlabel.handle_value ());
2493 adopt (title.handle_value ());
2497axes::properties::sync_positions (void)
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.
2505 if (activepositionproperty.is ("outerposition"))
2507 Matrix outpos = outerposition.get ().matrix_value ();
2508 Matrix defpos = default_axes_position ();
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);
2518 Matrix pos = position.get ().matrix_value ();
2519 pos(0) -= pos(2)*0.05;
2520 pos(1) -= pos(3)*0.05;
2523 outerposition = pos;
2527 update_transform ();
2531axes::properties::set_text_child (handle_property& hp,
2532 const std::string& who,
2533 const octave_value& v)
2535 graphics_handle val = ::reparent (v, "set", who, __myhandle__, false);
2539 xset (val, "handlevisibility", "off");
2541 gh_manager::free (hp.handle_value ());
2543 base_properties::remove_child (hp.handle_value ());
2547 adopt (hp.handle_value ());
2552axes::properties::set_xlabel (const octave_value& v)
2554 set_text_child (xlabel, "xlabel", v);
2558axes::properties::set_ylabel (const octave_value& v)
2560 set_text_child (ylabel, "ylabel", v);
2564axes::properties::set_zlabel (const octave_value& v)
2566 set_text_child (zlabel, "zlabel", v);
2570axes::properties::set_title (const octave_value& v)
2572 set_text_child (title, "title", v);
2576axes::properties::set_defaults (base_graphics_object& obj,
2577 const std::string& mode)
2584 colororder = default_colororder ();
2585 dataaspectratio = Matrix (1, 3, 1.0);
2586 dataaspectratiomode = "auto";
2589 Matrix tlim (1, 2, 0.0);
2595 Matrix cl (1, 2, 0);
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");
2632 yaxislocation = "left";
2633 xaxislocation = "bottom";
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 = "-";
2645 minorgridlinestyle = ":";
2646 // Note: plotboxaspectratio will be set through update_aspectratiors
2647 plotboxaspectratiomode = "auto";
2648 projection = "orthographic";
2650 tickdirmode = "auto";
2651 ticklength = default_axes_ticklength ();
2652 tightinset = Matrix (1, 4, 0.0);
2658 Matrix tview (1, 2, 0.0);
2663 nextplot = "replace";
2665 if (mode != "replace")
2667 fontangle = "normal";
2668 fontname = OCTAVE_DEFAULT_FONTNAME;
2670 fontunits = "points";
2671 fontweight = "normal";
2673 Matrix touterposition (1, 4, 0.0);
2674 touterposition(2) = 1;
2675 touterposition(3) = 1;
2676 outerposition = touterposition;
2678 position = default_axes_position ();
2680 activepositionproperty = "outerposition";
2685 children = Matrix ();
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);
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");
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");
2702 xset (xlabel.handle_value (), "verticalalignment", "cap");
2703 xset (ylabel.handle_value (), "verticalalignment", "bottom");
2704 xset (title.handle_value (), "verticalalignment", "bottom");
2706 xset (ylabel.handle_value (), "rotation", 90.0);
2707 xset (zlabel.handle_value (), "visible", "off");
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");
2714 adopt (xlabel.handle_value ());
2715 adopt (ylabel.handle_value ());
2716 adopt (zlabel.handle_value ());
2717 adopt (title.handle_value ());
2719 update_transform ();
2721 override_defaults (obj);
2725axes::properties::delete_text_child (handle_property& hp)
2727 graphics_handle h = hp.handle_value ();
2731 graphics_object go = gh_manager::get_object (h);
2733 if (go.valid_object ())
2734 gh_manager::free (h);
2736 base_properties::remove_child (h);
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.
2744 if (! is_beingdeleted ())
2746 hp = gh_manager::make_graphics_handle ("text", __myhandle__, false);
2748 xset (hp.handle_value (), "handlevisibility", "off");
2750 adopt (hp.handle_value ());
2755axes::properties::remove_child (const graphics_handle& h)
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);
2766 base_properties::remove_child (h);
2770base_properties::get_children (void) const
2772 Matrix retval = children;
2774 graphics_object go = gh_manager::get_object (0);
2776 root_figure::properties& props =
2777 dynamic_cast<root_figure::properties&> (go.get_properties ());
2779 if (! props.is_showhiddenhandles ())
2781 octave_idx_type k = 0;
2783 for (octave_idx_type i = 0; i < children.numel (); i++)
2785 graphics_handle kid = children (i);
2787 if (gh_manager::is_handle_visible (kid))
2788 retval(k++) = children(i);
2791 retval.resize (k, 1);
2800 Matrix m (4, 4, 0.0);
2801 for (int i = 0; i < 4; i++)
2809 ColumnVector v (4, 0.0);
2815xform_vector (double x, double y, double z)
2817 ColumnVector v (4, 1.0);
2818 v(0) = x; v(1) = y; v(2) = z;
2823transform (const Matrix& m, double x, double y, double z)
2825 return (m * xform_vector (x, y, z));
2829xform_scale (double x, double y, double z)
2831 Matrix m (4, 4, 0.0);
2832 m(0,0) = x; m(1,1) = y; m(2,2) = z; m(3,3) = 1;
2837xform_translate (double x, double y, double z)
2839 Matrix m = xform_matrix ();
2840 m(0,3) = x; m(1,3) = y; m(2,3) = z; m(3,3) = 1;
2845scale (Matrix& m, double x, double y, double z)
2847 m = m * xform_scale (x, y, z);
2851translate (Matrix& m, double x, double y, double z)
2853 m = m * xform_translate (x, y, z);
2857xform (ColumnVector& v, const Matrix& m)
2863scale (ColumnVector& v, double x, double y, double z)
2871translate (ColumnVector& v, double x, double y, double z)
2879normalize (ColumnVector& v)
2881 double fact = 1.0/sqrt(v(0)*v(0)+v(1)*v(1)+v(2)*v(2));
2882 scale (v, fact, fact, fact);
2886dot (const ColumnVector& v1, const ColumnVector& v2)
2888 return (v1(0)*v2(0)+v1(1)*v2(1)+v1(2)*v2(2));
2892norm (const ColumnVector& v)
2894 return sqrt (dot (v, v));
2898cross (const ColumnVector& v1, const ColumnVector& v2)
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);
2910 static double data[32] = {
2920 memcpy (m.fortran_vec (), data, sizeof(double)*32);
2925cam2xform (const Array<double>& m)
2927 ColumnVector retval (4, 1.0);
2928 memcpy (retval.fortran_vec (), m.fortran_vec (), sizeof(double)*3);
2933xform2cam (const ColumnVector& v)
2935 return v.extract_n (0, 3).transpose ();
2939axes::properties::update_camera (void)
2941 double xd = (xdir_is ("normal") ? 1 : -1);
2942 double yd = (ydir_is ("normal") ? 1 : -1);
2943 double zd = (zdir_is ("normal") ? 1 : -1);
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 ());
2949 double xo = xlimits(xd > 0 ? 0 : 1);
2950 double yo = ylimits(yd > 0 ? 0 : 1);
2951 double zo = zlimits(zd > 0 ? 0 : 1);
2953 Matrix pb = get_plotboxaspectratio ().matrix_value ();
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"));
2962 ColumnVector c_eye (xform_vector ());
2963 ColumnVector c_center (xform_vector ());
2964 ColumnVector c_upv (xform_vector ());
2966 if (cameratargetmode_is ("auto"))
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;
2972 cameratarget = xform2cam (c_center);
2975 c_center = cam2xform (get_cameratarget ().matrix_value ());
2977 if (camerapositionmode_is ("auto"))
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));
2983 if (el == 90 || el == -90)
2984 c_eye(2) = d*signum(el);
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);
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);
2997 cameraposition = xform2cam (c_eye);
3000 c_eye = cam2xform (get_cameraposition ().matrix_value ());
3002 if (cameraupvectormode_is ("auto"))
3004 Matrix tview = get_view ().matrix_value ();
3005 double az = tview(0), el = tview(1);
3007 if (el == 90 || el == -90)
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);
3015 cameraupvector = xform2cam (c_upv);
3018 c_upv = cam2xform (get_cameraupvector ().matrix_value ());
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 ();
3026 x_render = xform_matrix ();
3027 x_render_inv = xform_matrix ();
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);
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));
3041 ColumnVector F (c_center), f (F), UP (c_upv);
3045 if (std::abs (dot (f, UP)) > 1e-15)
3047 double fa = 1/sqrt(1-f(2)*f(2));
3048 scale (UP, fa, fa, fa);
3051 ColumnVector s = cross (f, UP);
3052 ColumnVector u = cross (s, f);
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);
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);
3069 Matrix bb = get_boundingbox (true);
3073 if (cameraviewanglemode_is ("auto"))
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);
3084 if ((bb(2)/bb(3)) > (xM/yM))
3089 v_angle = 2 * (180.0 / M_PI) * atan (1 / (2 * af * norm (F)));
3091 cameraviewangle = v_angle;
3094 v_angle = get_cameraviewangle ();
3096 double pf = 1 / (2 * tan ((v_angle / 2) * M_PI / 180.0) * norm (F));
3097 scale (x_projection, pf, pf, 1);
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);
3111 if ((bb(2)/bb(3)) > (xM/yM))
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);
3122 x_normrender = x_viewport * x_projection * x_view;
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);
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);
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;
3142 x_render_inv = x_render.inverse ();
3144 // Note: these matrices are a slight modified version of the regular
3145 // matrices, more suited for OpenGL rendering (x_gl_mat1 => light
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;
3155axes::properties::update_aspectratios (void)
3157 Matrix xlimits = get_xlim ().matrix_value ();
3158 Matrix ylimits = get_ylim ().matrix_value ();
3159 Matrix zlimits = get_zlim ().matrix_value ();
3161 double dx = (xlimits(1)-xlimits(0));
3162 double dy = (ylimits(1)-ylimits(0));
3163 double dz = (zlimits(1)-zlimits(0));
3165 if (dataaspectratiomode_is ("auto"))
3167 double dmin = xmin (xmin (dx, dy), dz);
3168 Matrix da (1, 3, 0.0);
3174 dataaspectratio = da;
3177 if (plotboxaspectratiomode_is ("auto"))
3179 if (dataaspectratiomode_is ("auto"))
3180 plotboxaspectratio = Matrix (1, 3, 1.0);
3183 Matrix da = get_dataaspectratio ().matrix_value ();
3184 Matrix pba (1, 3, 0.0);
3192 // FIXME -- if plotboxaspectratiomode is "manual", limits
3193 // and/or dataaspectratio might be adapted.
3196// The INTERNAL flag defines whether position or outerposition is used.
3199axes::properties::get_boundingbox (bool internal) const
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 ());
3208 pos = convert_position (pos, get_units (), "pixels",
3209 parent_bb.extract_n (0, 2, 1, 2));
3212 pos(1) = parent_bb(3) - pos(1) - pos(3);
3218graphics_xform::xform_vector (double x, double y, double z)
3220 return ::xform_vector (x, y, z);
3224graphics_xform::xform_eye (void)
3226 return ::xform_matrix ();
3230graphics_xform::transform (double x, double y, double z,
3231 bool use_scale) const
3240 return ::transform (xform, x, y, z);
3244graphics_xform::untransform (double x, double y, double z,
3245 bool use_scale) const
3247 ColumnVector v = ::transform (xform_inv, x, y, z);
3251 v(0) = sx.unscale (v(0));
3252 v(1) = sy.unscale (v(1));
3253 v(2) = sz.unscale (v(2));
3260axes::get_default (const caseless_str& name) const
3262 octave_value retval = default_properties.lookup (name);
3264 if (retval.is_undefined ())
3266 graphics_handle parent = get_parent ();
3267 graphics_object parent_obj = gh_manager::get_object (parent);
3269 retval = parent_obj.get_default (name);
3276// FIXME -- maybe this should go into array_property class?
3279check_limit_vals (double& min_val, double& max_val, double& min_pos,
3280 const array_property& data)
3282 double val = data.min_val ();
3283 if (! (xisinf (val) || xisnan (val)) && val < min_val)
3285 val = data.max_val ();
3286 if (! (xisinf (val) || xisnan (val)) && val > max_val)
3288 val = data.min_pos ();
3289 if (! (xisinf (val) || xisnan (val)) && val > 0 && val < min_pos)
3295check_limit_vals (double& min_val, double& max_val, double& min_pos,
3296 const octave_value& data)
3298 if (data.is_matrix_type ())
3300 Matrix m = data.matrix_value ();
3302 if (! error_state && m.numel () == 3)
3307 if (! (xisinf (val) || xisnan (val)) && val < min_val)
3311 if (! (xisinf (val) || xisnan (val)) && val > max_val)
3315 if (! (xisinf (val) || xisnan (val)) && val > 0 && val < min_pos)
3321// magform(x) Returns (a, b), where x = a * 10^b, a >= 1., and b is
3325magform (double x, double& a, int& b)
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);
3349// A translation from Tom Holoryd's python code at
3350// http://kurage.nimh.nih.gov/tomh/tics.py
3351// FIXME -- add log ticks
3354axes::properties::calc_tick_sep (double lo, double hi)
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.
3366 magform ((hi-lo)/ticint, a, b);
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);
3374 else if (a < sqrt_10)
3376 else if (a < sqrt_50)
3381 return x * std::pow (10., b);
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
3390axes::properties::get_axis_limits (double xmin, double xmax,
3391 double min_pos, bool logscale)
3395 double min_val = xmin;
3396 double max_val = xmax;
3398 if (! (xisinf (min_val) || xisinf (max_val)))
3402 if (xisinf (min_pos))
3404 // warning ("axis: logscale with no positive values to plot");
3410 warning ("axis: omitting nonpositive data in log plot");
3413 // FIXME -- maybe this test should also be relative?
3414 if (std::abs (min_val - max_val) < sqrt (DBL_EPSILON))
3419 min_val = pow (10, floor (log10 (min_val)));
3420 max_val = pow (10, ceil (log10 (max_val)));
3424 if (min_val == 0 && max_val == 0)
3429 // FIXME -- maybe this test should also be relative?
3430 else if (std::abs (min_val - max_val) < sqrt (DBL_EPSILON))
3432 min_val -= 0.1 * std::abs (min_val);
3433 max_val += 0.1 * std::abs (max_val);
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);
3442 retval.resize (1, 2);
3444 retval(0) = min_val;
3445 retval(1) = max_val;
3451axes::properties::calc_ticks_and_lims (array_property& lims,
3452 array_property& ticks,
3453 bool limmode_is_auto, bool is_logscale)
3455 // FIXME -- add log ticks and lims
3457 if (lims.get ().is_empty ())
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)
3472 // FIXME we should check for negtives here
3473 hi = std::log10 (hi);
3474 lo = std::log10 (lo);
3477 double tick_sep = calc_tick_sep (lo , hi);
3479 int i1 = static_cast<int> (std::floor (lo / tick_sep));
3480 int i2 = static_cast<int> (std::ceil (hi / tick_sep));
3482 if (limmode_is_auto)
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;