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");
1398// Set properties given as a cs-list of name, value pairs.
1401graphics_object::set (const octave_value_list& args)
1403 int nargin = args.length ();
1407 else if (nargin % 2 == 0)
1409 for (int i = 0; i < nargin; i += 2)
1411 caseless_str name = args(i).string_value ();
1415 octave_value val = args(i+1);
1417 set_value_or_default (name, val);
1423 error ("set: expecting argument %d to be a property name", i);
1427 error ("set: invalid number of arguments");
1431%!# test set with name, value pairs
1433%! set(gcf, "visible", "off");
1434%! h = plot (1:10, 10:-1:1);
1435%! set (h, "linewidth", 10, "marker", "x");
1436%! assert (get (h, "linewidth"), 10);
1437%! assert (get (h, "marker"), "x");
1440// Set properties given in two cell arrays containing names and values.
1442graphics_object::set (const Array<std::string>& names,
1443 const Cell& values, octave_idx_type row)
1445 if (names.numel () != values.columns ())
1447 error("set: number of names must match number of value columns (%d != %d)",
1448 names.numel (), values.columns ());
1451 octave_idx_type k = names.columns ();
1453 for (octave_idx_type column = 0; column < k; column++)
1455 caseless_str name = names(column);
1456 octave_value val = values(row, column);
1458 set_value_or_default (name, val);
1466%!# test set with cell array arguments
1468%! set (gcf, "visible", "off");
1469%! h = plot (1:10, 10:-1:1);
1470%! set (h, {"linewidth", "marker"}, {10, "x"});
1471%! assert (get(h, "linewidth"), 10);
1472%! assert (get(h, "marker"), "x");
1474%!# test set with multiple handles and cell array arguments
1476%! set (gcf, "visible", "off");
1477%! h = plot (1:10, 10:-1:1, 1:10, 1:10);
1478%! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"});
1479%! assert (get (h, "linewidth"), {10; 5});
1480%! assert (get (h, "marker"), {"x"; "o"});
1481%! set (h, {"linewidth", "marker"}, {10, "x"});
1482%! assert (get (h, "linewidth"), {10; 10});
1483%! assert (get (h, "marker"), {"x"; "x"});
1485%!error <set: number of graphics handles must match number of value rows>
1486%! set (gcf, "visible", "off");
1487%! h = plot (1:10, 10:-1:1, 1:10, 1:10);
1488%! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"; 7, "."});
1490%!error <set: number of names must match number of value columns>
1491%! set (gcf, "visible", "off");
1492%! h = plot (1:10, 10:-1:1, 1:10, 1:10);
1493%! set (h, {"linewidth"}, {10, "x"; 5, "o"});
1496// Set properties given in a struct array
1498graphics_object::set (const Octave_map& m)
1500 for (Octave_map::const_iterator p = m.begin ();
1503 caseless_str name = m.key (p);
1505 octave_value val = octave_value (m.contents (p).elem (m.numel () - 1));
1507 set_value_or_default (name, val);
1515%!# test set with struct arguments
1517%! set (gcf, "visible", "off");
1518%! h = plot (1:10, 10:-1:1);
1519%! set (h, struct ("linewidth", 10, "marker", "x"));
1520%! assert (get (h, "linewidth"), 10);
1521%! assert (get (h, "marker"), "x");
1522%! h = plot (1:10, 10:-1:1, 1:10, 1:10);
1523%! set (h, struct ("linewidth", {5, 10}));
1524%! assert (get(h, "linewidth"), {10; 10});
1527// Set a property to a value or to its (factory) default value.
1530graphics_object::set_value_or_default (const caseless_str& name,
1531 const octave_value& val)
1533 if (val.is_string ())
1535 caseless_str tval = val.string_value ();
1537 octave_value default_val;
1539 if (tval.compare ("default"))
1541 default_val = get_default (name);
1546 rep->set (name, default_val);
1548 else if (tval.compare ("factory"))
1550 default_val = get_factory_default (name);
1555 rep->set (name, default_val);
1558 rep->set (name, val);
1561 rep->set (name, val);
1565%!# test setting of default values
1567%! set (gcf, "visible", "off");
1568%! h = plot (1:10, 10:-1:1);
1569%! set (0, "defaultlinelinewidth", 20);
1570%! set (h, "linewidth", "default");
1571%! assert (get (h, "linewidth"), 20);
1572%! set (h, "linewidth", "factory");
1573%! assert (get (h, "linewidth"), 0.5);
1577make_handle_fraction (void)
1579 static double maxrand = RAND_MAX + 2.0;
1581 return (rand () + 1.0) / maxrand;
1585gh_manager::get_handle (const std::string& go_name)
1587 graphics_handle retval;
1589 if (go_name == "figure")
1591 // Figure handles are positive integers corresponding to the
1594 // We always want the lowest unused figure number.
1598 while (handle_map.find (retval) != handle_map.end ())
1603 // Other graphics handles are negative integers plus some random
1604 // fractional part. To avoid running out of integers, we
1605 // recycle the integer part but tack on a new random part each
1608 free_list_iterator p = handle_free_list.begin ();
1610 if (p != handle_free_list.end ())
1613 handle_free_list.erase (p);
1617 retval = graphics_handle (next_handle);
1619 next_handle = ceil (next_handle) - 1.0 - make_handle_fraction ();
1627gh_manager::do_free (const graphics_handle& h)
1631 if (h.value () != 0)
1633 iterator p = handle_map.find (h);
1635 if (p != handle_map.end ())
1637 base_properties& bp = p->second.get_properties ();
1639 bp.set_beingdeleted (true);
1641 bp.delete_children ();
1643 octave_value val = bp.get_deletefcn ();
1645 bp.execute_deletefcn ();
1648 graphics_backend backend = p->second.get_backend ();
1650 backend.object_destroyed (p->second);
1652 // Note: this will be valid only for first explicitly
1653 // deleted object. All its children will then have an
1656 // Graphics handles for non-figure objects are negative
1657 // integers plus some random fractional part. To avoid
1658 // running out of integers, we recycle the integer part
1659 // but tack on a new random part each time.
1661 handle_map.erase (p);
1664 handle_free_list.insert (ceil (h.value ()) - make_handle_fraction ());
1667 error ("graphics_handle::free: invalid object %g", h.value ());
1670 error ("graphics_handle::free: can't delete root figure");
1674gh_manager *gh_manager::instance = 0;
1677xset (const graphics_handle& h, const caseless_str& name,
1678 const octave_value& val)
1680 graphics_object obj = gh_manager::get_object (h);
1681 obj.set (name, val);
1685xset (const graphics_handle& h, const octave_value_list& args)
1687 if (args.length () > 0)
1689 graphics_object obj = gh_manager::get_object (h);
1696xget (const graphics_handle& h, const caseless_str& name)
1698 graphics_object obj = gh_manager::get_object (h);
1699 return obj.get (name);
1702static graphics_handle
1703reparent (const octave_value& ov, const std::string& who,
1704 const std::string& property, const graphics_handle& new_parent,
1707 graphics_handle h = octave_NaN;
1709 double val = ov.double_value ();
1713 h = gh_manager::lookup (val);
1717 graphics_object obj = gh_manager::get_object (h);
1719 graphics_handle parent_h = obj.get_parent ();
1721 graphics_object parent_obj = gh_manager::get_object (parent_h);
1723 parent_obj.remove_child (h);
1726 obj.set ("parent", new_parent.value ());
1728 obj.reparent (new_parent);
1731 error ("%s: invalid graphics handle (= %g) for %s",
1732 who.c_str (), val, property.c_str ());
1735 error ("%s: expecting %s to be a graphics handle",
1736 who.c_str (), property.c_str ());
1741// This function is NOT equivalent to the scripting language function gcf.
1745 octave_value val = xget (0, "currentfigure");
1747 return val.is_empty () ? octave_NaN : val.double_value ();
1750// This function is NOT equivalent to the scripting language function gca.
1754 octave_value val = xget (gcf (), "currentaxes");
1756 return val.is_empty () ? octave_NaN : val.double_value ();
1760adopt (const graphics_handle& p, const graphics_handle& h)
1762 graphics_object parent_obj = gh_manager::get_object (p);
1764 parent_obj.adopt (h);
1768is_handle (const graphics_handle& h)
1774is_handle (double val)
1776 graphics_handle h = gh_manager::lookup (val);
1782is_handle (const octave_value& val)
1784 octave_value retval = false;
1786 if (val.is_real_scalar () && is_handle (val.double_value ()))
1788 else if (val.is_real_matrix ())
1790 if (val.is_string ())
1791 retval = boolNDArray (val.dims (), false);
1794 const NDArray handles = val.array_value ();
1798 boolNDArray result (handles.dims ());
1800 for (octave_idx_type i = 0; i < handles.numel (); i++)
1801 result.xelem (i) = is_handle (handles (i));
1812is_figure (double val)
1814 graphics_object obj = gh_manager::get_object (val);
1816 return obj && obj.isa ("figure");
1820xcreatefcn (const graphics_handle& h)
1822 graphics_object obj = gh_manager::get_object (h);
1823 obj.get_properties ().execute_createfcn ();
1826// ---------------------------------------------------------------------
1829base_graphics_backend::property_changed (const graphics_handle& h, int id)
1831 graphics_object go = gh_manager::get_object (h);
1833 property_changed (go, id);
1837base_graphics_backend::object_created (const graphics_handle& h)
1839 graphics_object go = gh_manager::get_object (h);
1841 object_created (go);
1845base_graphics_backend::object_destroyed (const graphics_handle& h)
1847 graphics_object go = gh_manager::get_object (h);
1849 object_destroyed (go);
1851// ---------------------------------------------------------------------
1854base_properties::set_from_list (base_graphics_object& obj,
1855 property_list& defaults)
1857 std::string go_name = graphics_object_name ();
1859 property_list::plist_map_const_iterator p = defaults.find (go_name);
1861 if (p != defaults.end ())
1863 const property_list::pval_map_type pval_map = p->second;
1865 for (property_list::pval_map_const_iterator q = pval_map.begin ();
1866 q != pval_map.end ();
1869 std::string pname = q->first;
1871 obj.set (pname, q->second);
1875 error ("error setting default property %s", pname.c_str ());
1883base_properties::get_dynamic (const caseless_str& name) const
1885 octave_value retval;
1887 std::map<caseless_str, property, cmp_caseless_str>::const_iterator it = all_props.find (name);
1889 if (it != all_props.end ())
1890 retval = it->second.get ();
1892 error ("get: unknown property \"%s\"", name.c_str ());
1898base_properties::get_dynamic (bool all) const
1902 for (std::map<caseless_str, property, cmp_caseless_str>::const_iterator it = all_props.begin ();
1903 it != all_props.end (); ++it)
1904 if (all || ! it->second.is_hidden ())
1905 m.assign (it->second.get_name (), it->second.get ());
1910std::set<std::string>
1911base_properties::dynamic_property_names (void) const
1913 return dynamic_properties;
1917base_properties::has_dynamic_property (const std::string& pname)
1919 const std::set<std::string>& dynprops = dynamic_property_names ();
1921 return dynprops.find (pname) != dynprops.end ();
1925base_properties::set_dynamic (const caseless_str& pname,
1926 const octave_value& val)
1928 std::map<caseless_str, property, cmp_caseless_str>::iterator it = all_props.find (pname);
1930 if (it != all_props.end ())
1931 it->second.set (val);
1933 error ("set: unknown property \"%s\"", pname.c_str ());
1937 dynamic_properties.insert (pname);
1944base_properties::get_property_dynamic (const caseless_str& name)
1946 std::map<caseless_str, property, cmp_caseless_str>::const_iterator it = all_props.find (name);
1948 if (it == all_props.end ())
1950 error ("get_property: unknown property \"%s\"", name.c_str ());
1958base_properties::remove_child (const graphics_handle& h)
1960 octave_idx_type k = -1;
1961 octave_idx_type n = children.numel ();
1962 for (octave_idx_type i = 0; i < n; i++)
1964 if (h.value () == children(i))
1973 Matrix new_kids (n-1, 1);
1974 octave_idx_type j = 0;
1975 for (octave_idx_type i = 0; i < n; i++)
1978 new_kids(j++) = children(i);
1980 children = new_kids;
1986base_properties::set_parent (const octave_value& val)
1988 double tmp = val.double_value ();
1990 graphics_handle new_parent = octave_NaN;
1994 new_parent = gh_manager::lookup (tmp);
1996 if (new_parent.ok ())
1998 graphics_object parent_obj = gh_manager::get_object (get_parent ());
2000 parent_obj.remove_child (__myhandle__);
2002 parent = new_parent.as_octave_value ();
2004 ::adopt (parent.handle_value (), __myhandle__);
2007 error ("set: invalid graphics handle (= %g) for parent", tmp);
2010 error ("set: expecting parent to be a graphics handle");
2014base_properties::set_children (const octave_value& val)
2016 const Matrix new_kids = val.matrix_value ();
2022 const Matrix visible_kids = get_children ();
2024 if (visible_kids.numel () == new_kids.numel ())
2026 Matrix t1 = visible_kids.sort ();
2027 Matrix t2 = new_kids.sort ();
2036 error ("set: new children must be a permutation of existing children");
2041 error ("set: expecting children to be array of graphics handles");
2045 children = new_kids.stack (get_hidden_children ());
2049base_properties::mark_modified (void)
2051 __modified__ = "on";
2052 graphics_object parent_obj = gh_manager::get_object (get_parent ());
2054 parent_obj.mark_modified ();
2058base_properties::override_defaults (base_graphics_object& obj)
2060 graphics_object parent_obj = gh_manager::get_object (get_parent ());
2063 parent_obj.override_defaults (obj);
2067base_properties::update_axis_limits (const std::string& axis_type) const
2069 graphics_object obj = gh_manager::get_object (__myhandle__);
2072 obj.update_axis_limits (axis_type);
2076base_properties::delete_children (void)
2078 octave_idx_type n = children.numel ();
2080 // A callback function might have already deleted the child,
2081 // so check before deleting
2082 for (octave_idx_type i = 0; i < n; i++)
2084 graphics_object go = gh_manager::get_object (children(i));
2086 if (go.valid_object ())
2087 gh_manager::free (children(i));
2092base_properties::get_backend (void) const
2094 graphics_object go = gh_manager::get_object (get_parent ());
2097 return go.get_backend ();
2099 return graphics_backend ();
2103base_properties::update_boundingbox (void)
2105 Matrix kids = get_children ();
2107 for (int i = 0; i < kids.numel (); i++)
2109 graphics_object go = gh_manager::get_object (kids(i));
2111 if (go.valid_object ())
2112 go.get_properties ().update_boundingbox ();
2117base_properties::add_listener (const caseless_str& nm, const octave_value& v,
2120 property p = get_property (nm);
2122 if (! error_state && p.ok ())
2123 p.add_listener (v, mode);
2127base_properties::delete_listener (const caseless_str& nm,
2128 const octave_value& v, listener_mode mode)
2130 property p = get_property (nm);
2132 if (! error_state && p.ok ())
2133 p.delete_listener (v, mode);
2136// ---------------------------------------------------------------------
2138class gnuplot_backend : public base_graphics_backend
2141 gnuplot_backend (void)
2142 : base_graphics_backend ("gnuplot") { }
2144 ~gnuplot_backend (void) { }
2146 bool is_valid (void) const { return true; }
2148 void object_destroyed (const graphics_object& go)
2150 if (go.isa ("figure"))
2152 const figure::properties& props =
2153 dynamic_cast<const figure::properties&> (go.get_properties ());
2155 send_quit (props.get___plot_stream__ ());
2159 void property_changed (const graphics_object& go, int id)
2161 if (go.isa ("figure"))
2163 graphics_object obj (go);
2165 figure::properties& props =
2166 dynamic_cast<figure::properties&> (obj.get_properties ());
2170 case base_properties::VISIBLE:
2171 if (! props.is_visible ())
2173 send_quit (props.get___plot_stream__ ());
2174 props.set___plot_stream__ (Matrix ());
2175 props.set___enhanced__ (false);
2182 void redraw_figure (const graphics_object& go) const
2184 octave_value_list args;
2185 args(0) = go.get_handle ().as_octave_value ();
2186 feval ("gnuplot_drawnow", args);
2189 void print_figure (const graphics_object& go, const std::string& term,
2190 const std::string& file, bool mono,
2191 const std::string& debug_file) const
2193 octave_value_list args;
2194 if (! debug_file.empty ())
2195 args(4) = debug_file;
2199 args(0) = go.get_handle ().as_octave_value ();
2200 feval ("gnuplot_drawnow", args);
2203 Matrix get_canvas_size (const graphics_handle&) const
2205 Matrix sz (1, 2, 0.0);
2209 double get_screen_resolution (void) const
2212 Matrix get_screen_size (void) const
2213 { return Matrix (1, 2, 0.0); }
2216 void send_quit (const octave_value& pstream) const
2218 if (! pstream.is_empty ())
2220 octave_value_list args;
2221 Matrix fids = pstream.matrix_value ();
2225 args(1) = "\nquit;\n";
2227 feval ("fputs", args);
2230 feval ("fflush", args);
2231 feval ("pclose", args);
2233 if (fids.numel () > 1)
2236 feval ("pclose", args);
2238 if (fids.numel () > 2)
2241 feval ("waitpid", args);
2250graphics_backend::default_backend (void)
2252 if (available_backends.size () == 0)
2253 register_backend (new gnuplot_backend ());
2255 return available_backends["gnuplot"];
2258std::map<std::string, graphics_backend> graphics_backend::available_backends;
2260// ---------------------------------------------------------------------
2263base_graphics_object::update_axis_limits (const std::string& axis_type)
2265 if (valid_object ())
2267 graphics_object parent_obj = gh_manager::get_object (get_parent ());
2270 parent_obj.update_axis_limits (axis_type);
2273 error ("base_graphics_object::update_axis_limits: invalid graphics object");
2277base_graphics_object::remove_all_listeners (void)
2279 Octave_map m = get (true).map_value ();
2281 for (Octave_map::const_iterator pa = m.begin (); pa != m.end (); pa++)
2283 // FIXME -- there has to be a better way. I think we want to
2284 // ask whether it is OK to delete the listener for the given
2285 // property. How can we know in advance that it will be OK?
2287 unwind_protect frame;
2289 frame.protect_var (discard_error_messages);
2290 frame.protect_var (error_state);
2292 discard_error_messages = true;
2294 property p = get_properties ().get_property (pa->first);
2296 if (! error_state && p.ok ())
2297 p.delete_listener ();
2301// ---------------------------------------------------------------------
2303#include "graphics-props.cc"
2305// ---------------------------------------------------------------------
2308root_figure::properties::set_currentfigure (const octave_value& v)
2310 graphics_handle val (v);
2315 if (xisnan (val.value ()) || is_handle (val))
2317 currentfigure = val;
2319 gh_manager::push_figure (val);
2322 gripe_set_invalid ("currentfigure");
2326root_figure::properties::set_callbackobject (const octave_value& v)
2328 graphics_handle val (v);
2333 if (xisnan (val.value ()))
2335 if (! cbo_stack.empty ())
2337 val = cbo_stack.front ();
2339 cbo_stack.pop_front ();
2342 callbackobject = val;
2344 else if (is_handle (val))
2346 if (get_callbackobject ().ok ())
2347 cbo_stack.push_front (get_callbackobject ());
2349 callbackobject = val;
2352 gripe_set_invalid ("callbackobject");
2356root_figure::properties::update_units (void)
2358 caseless_str xunits = get_units ();
2360 Matrix ss = default_screensize ();
2362 double dpi = get_screenpixelsperinch ();
2364 if (xunits.compare ("inches"))
2371 else if (xunits.compare ("centimeters"))
2375 ss(2) *= 2.54 / dpi;
2376 ss(3) *= 2.54 / dpi;
2378 else if (xunits.compare ("normalized"))
2380 ss = Matrix (1, 4, 1.0);
2382 else if (xunits.compare ("points"))
2390 set_screensize (ss);
2394root_figure::properties::remove_child (const graphics_handle& gh)
2396 gh_manager::pop_figure (gh);
2398 graphics_handle cf = gh_manager::current_figure ();
2400 xset (0, "currentfigure", cf.value ());
2402 base_properties::remove_child (gh);
2406root_figure::factory_properties = root_figure::init_factory_properties ();
2408// ---------------------------------------------------------------------
2411figure::properties::set_currentaxes (const octave_value& v)
2413 graphics_handle val (v);
2418 if (xisnan (val.value ()) || is_handle (val))
2421 gripe_set_invalid ("currentaxes");
2425figure::properties::remove_child (const graphics_handle& gh)
2427 base_properties::remove_child (gh);
2429 if (gh == currentaxes.handle_value ())
2431 graphics_handle new_currentaxes;
2433 for (octave_idx_type i = 0; i < children.numel (); i++)
2435 graphics_handle kid = children(i);
2437 graphics_object go = gh_manager::get_object (kid);
2439 if (go.isa ("axes"))
2441 new_currentaxes = kid;
2446 currentaxes = new_currentaxes;
2451figure::properties::set_visible (const octave_value& val)
2453 std::string s = val.string_value ();
2458 xset (0, "currentfigure", __myhandle__.value ());
2465figure::properties::get_boundingbox (bool) const
2467 Matrix screen_size = xget (0, "screensize").matrix_value ().extract_n (0, 2, 1, 2);
2470 pos = convert_position (get_position ().matrix_value (), get_units (),
2471 "pixels", screen_size);
2475 pos(1) = screen_size(1) - pos(1) - pos(3);
2481figure::properties::set_boundingbox (const Matrix& bb)
2483 Matrix screen_size = xget (0, "screensize").matrix_value ().extract_n (0, 2, 1, 2);
2486 pos(1) = screen_size(1) - pos(1) - pos(3);
2489 pos = convert_position (pos, "pixels", get_units (), screen_size);
2495figure::properties::set_position (const octave_value& v)
2499 Matrix old_bb, new_bb;
2501 old_bb = get_boundingbox ();
2503 new_bb = get_boundingbox ();
2505 if (old_bb != new_bb)
2507 if (old_bb(2) != new_bb(2) || old_bb(3) != new_bb(3))
2509 execute_resizefcn ();
2510 update_boundingbox ();
2519figure::properties::get_title (void) const
2521 if (is_numbertitle ())
2523 std::ostringstream os;
2524 std::string nm = get_name ();
2526 os << "Figure " << __myhandle__.value ();
2528 os << ": " << get_name ();
2537figure::get_default (const caseless_str& name) const
2539 octave_value retval = default_properties.lookup (name);
2541 if (retval.is_undefined ())
2543 graphics_handle parent = get_parent ();
2544 graphics_object parent_obj = gh_manager::get_object (parent);
2546 retval = parent_obj.get_default (name);
2552// ---------------------------------------------------------------------
2555axes::properties::init (void)
2557 position.add_constraint (dim_vector (1, 4));
2558 position.add_constraint (dim_vector (0, 0));
2559 outerposition.add_constraint (dim_vector (1, 4));
2560 colororder.add_constraint (dim_vector (-1, 3));
2561 dataaspectratio.add_constraint (dim_vector (1, 3));
2562 plotboxaspectratio.add_constraint (dim_vector (1, 3));
2563 xlim.add_constraint (2);
2564 ylim.add_constraint (2);
2565 zlim.add_constraint (2);
2566 clim.add_constraint (2);
2567 alim.add_constraint (2);
2568 xtick.add_constraint (dim_vector (1, -1));
2569 ytick.add_constraint (dim_vector (1, -1));
2570 ztick.add_constraint (dim_vector (1, -1));
2571 Matrix vw (1, 2, 0);
2574 view.add_constraint (dim_vector (1, 2));
2575 cameraposition.add_constraint (dim_vector (1, 3));
2576 Matrix upv (1, 3, 0.0);
2578 cameraupvector = upv;
2579 cameraupvector.add_constraint (dim_vector (1, 3));
2580 currentpoint.add_constraint (dim_vector (2, 3));
2581 ticklength.add_constraint (dim_vector (1, 2));
2582 tightinset.add_constraint (dim_vector (1, 4));
2584 x_zlim.resize (1, 2);
2590 calc_ticklabels (xtick, xticklabel, xscale.is ("log"));
2591 calc_ticklabels (ytick, yticklabel, yscale.is ("log"));
2592 calc_ticklabels (ztick, zticklabel, zscale.is ("log"));
2594 xset (xlabel.handle_value (), "handlevisibility", "off");
2595 xset (ylabel.handle_value (), "handlevisibility", "off");
2596 xset (zlabel.handle_value (), "handlevisibility", "off");
2597 xset (title.handle_value (), "handlevisibility", "off");
2599 xset (xlabel.handle_value (), "horizontalalignment", "center");
2600 xset (ylabel.handle_value (), "horizontalalignment", "center");
2601 xset (zlabel.handle_value (), "horizontalalignment", "right");
2602 xset (title.handle_value (), "horizontalalignment", "center");
2604 xset (xlabel.handle_value (), "verticalalignment", "cap");
2605 xset (ylabel.handle_value (), "verticalalignment", "bottom");
2606 xset (title.handle_value (), "verticalalignment", "bottom");
2608 xset (ylabel.handle_value (), "rotation", 90.0);
2609 xset (zlabel.handle_value (), "visible", "off");
2611 xset (xlabel.handle_value (), "clipping", "off");
2612 xset (ylabel.handle_value (), "clipping", "off");
2613 xset (zlabel.handle_value (), "clipping", "off");
2614 xset (title.handle_value (), "clipping", "off");
2616 adopt (xlabel.handle_value ());
2617 adopt (ylabel.handle_value ());
2618 adopt (zlabel.handle_value ());
2619 adopt (title.handle_value ());
2623axes::properties::sync_positions (void)
2626 // FIXME -- this should take font metrics into consideration,
2627 // and also the fact that the colorbox leaves the outerposition
2628 // alone but alters the position. For now just don't adjust the
2629 // positions relative to each other.
2631 if (activepositionproperty.is ("outerposition"))
2633 Matrix outpos = outerposition.get ().matrix_value ();
2634 Matrix defpos = default_axes_position ();
2636 pos(0) = outpos(0) + defpos(0) * outpos(2);
2637 pos(1) = outpos(1) + defpos(1) * outpos(3);
2638 pos(2) = outpos(2) * defpos(2);
2639 pos(3) = outpos(3) * defpos(3);
2644 Matrix pos = position.get ().matrix_value ();
2645 pos(0) -= pos(2)*0.05;
2646 pos(1) -= pos(3)*0.05;
2649 outerposition = pos;
2653 update_transform ();
2657axes::properties::set_text_child (handle_property& hp,
2658 const std::string& who,
2659 const octave_value& v)
2661 graphics_handle val;
2665 val = gh_manager::make_graphics_handle ("text", __myhandle__, false);
2667 xset (val, "string", v);
2671 graphics_object go = gh_manager::get_object (gh_manager::lookup (v));
2673 if (go.isa ("text"))
2674 val = ::reparent (v, "set", who, __myhandle__, false);
2677 std::string cname = v.class_name ();
2679 error ("set: expecting text graphics object or character string for %s property, found %s",
2680 who.c_str (), cname.c_str ());
2686 xset (val, "handlevisibility", "off");
2688 gh_manager::free (hp.handle_value ());
2690 base_properties::remove_child (hp.handle_value ());
2694 adopt (hp.handle_value ());
2699axes::properties::set_xlabel (const octave_value& v)
2701 set_text_child (xlabel, "xlabel", v);
2705axes::properties::set_ylabel (const octave_value& v)
2707 set_text_child (ylabel, "ylabel", v);
2711axes::properties::set_zlabel (const octave_value& v)
2713 set_text_child (zlabel, "zlabel", v);
2717axes::properties::set_title (const octave_value& v)
2719 set_text_child (title, "title", v);
2723axes::properties::set_defaults (base_graphics_object& obj,
2724 const std::string& mode)
2731 colororder = default_colororder ();
2732 dataaspectratio = Matrix (1, 3, 1.0);
2733 dataaspectratiomode = "auto";
2736 Matrix tlim (1, 2, 0.0);
2742 Matrix cl (1, 2, 0);
2766 xticklabelmode = "auto";
2767 yticklabelmode = "auto";
2768 zticklabelmode = "auto";
2769 color = color_values (1, 1, 1);
2770 xcolor = color_values ("black");
2771 ycolor = color_values ("black");
2772 zcolor = color_values ("black");
2779 yaxislocation = "left";
2780 xaxislocation = "bottom";
2782 // Note: camera properties will be set through update_transform
2783 camerapositionmode = "auto";
2784 cameratargetmode = "auto";
2785 cameraupvectormode = "auto";
2786 cameraviewanglemode = "auto";
2787 plotboxaspectratio = Matrix (1, 3, 1.0);
2788 drawmode = "normal";
2789 gridlinestyle = ":";
2790 linestyleorder = "-";
2792 minorgridlinestyle = ":";
2793 // Note: plotboxaspectratio will be set through update_aspectratiors
2794 plotboxaspectratiomode = "auto";
2795 projection = "orthographic";
2797 tickdirmode = "auto";
2798 ticklength = default_axes_ticklength ();
2799 tightinset = Matrix (1, 4, 0.0);
2805 Matrix tview (1, 2, 0.0);
2810 nextplot = "replace";
2812 if (mode != "replace")
2814 fontangle = "normal";
2815 fontname = OCTAVE_DEFAULT_FONTNAME;
2817 fontunits = "points";
2818 fontweight = "normal";
2820 Matrix touterposition (1, 4, 0.0);
2821 touterposition(2) = 1;
2822 touterposition(3) = 1;
2823 outerposition = touterposition;
2825 position = default_axes_position ();
2827 activepositionproperty = "outerposition";
2832 children = Matrix ();
2834 xlabel = gh_manager::make_graphics_handle ("text", __myhandle__, false);
2835 ylabel = gh_manager::make_graphics_handle ("text", __myhandle__, false);
2836 zlabel = gh_manager::make_graphics_handle ("text", __myhandle__, false);
2837 title = gh_manager::make_graphics_handle ("text", __myhandle__, false);
2839 xset (xlabel.handle_value (), "handlevisibility", "off");
2840 xset (ylabel.handle_value (), "handlevisibility", "off");
2841 xset (zlabel.handle_value (), "handlevisibility", "off");
2842 xset (title.handle_value (), "handlevisibility", "off");
2844 xset (xlabel.handle_value (), "horizontalalignment", "center");
2845 xset (ylabel.handle_value (), "horizontalalignment", "center");
2846 xset (zlabel.handle_value (), "horizontalalignment", "right");
2847 xset (title.handle_value (), "horizontalalignment", "center");
2849 xset (xlabel.handle_value (), "verticalalignment", "cap");
2850 xset (ylabel.handle_value (), "verticalalignment", "bottom");
2851 xset (title.handle_value (), "verticalalignment", "bottom");
2853 xset (ylabel.handle_value (), "rotation", 90.0);
2854 xset (zlabel.handle_value (), "visible", "off");
2856 xset (xlabel.handle_value (), "clipping", "off");
2857 xset (ylabel.handle_value (), "clipping", "off");
2858 xset (zlabel.handle_value (), "clipping", "off");
2859 xset (title.handle_value (), "clipping", "off");
2861 adopt (xlabel.handle_value ());
2862 adopt (ylabel.handle_value ());
2863 adopt (zlabel.handle_value ());
2864 adopt (title.handle_value ());
2866 update_transform ();
2868 override_defaults (obj);
2872axes::properties::delete_text_child (handle_property& hp)
2874 graphics_handle h = hp.handle_value ();
2878 graphics_object go = gh_manager::get_object (h);
2880 if (go.valid_object ())
2881 gh_manager::free (h);
2883 base_properties::remove_child (h);
2886 // FIXME -- is it necessary to check whether the axes object is
2887 // being deleted now? I think this function is only called when an
2888 // individual child object is delete and not when the parent axes
2889 // object is deleted.
2891 if (! is_beingdeleted ())
2893 hp = gh_manager::make_graphics_handle ("text", __myhandle__, false);
2895 xset (hp.handle_value (), "handlevisibility", "off");
2897 adopt (hp.handle_value ());
2902axes::properties::remove_child (const graphics_handle& h)
2904 if (xlabel.handle_value ().ok () && h == xlabel.handle_value ())
2905 delete_text_child (xlabel);
2906 else if (ylabel.handle_value ().ok () && h == ylabel.handle_value ())
2907 delete_text_child (ylabel);
2908 else if (zlabel.handle_value ().ok () && h == zlabel.handle_value ())
2909 delete_text_child (zlabel);
2910 else if (title.handle_value ().ok () && h == title.handle_value ())
2911 delete_text_child (title);
2913 base_properties::remove_child (h);
2917base_properties::get_children_internal (bool return_hidden) const
2919 Matrix retval = children;
2921 graphics_object go = gh_manager::get_object (0);
2923 root_figure::properties& props =
2924 dynamic_cast<root_figure::properties&> (go.get_properties ());
2926 if (! props.is_showhiddenhandles ())
2928 octave_idx_type k = 0;
2930 for (octave_idx_type i = 0; i < children.numel (); i++)
2932 graphics_handle kid = children (i);
2934 if (gh_manager::is_handle_visible (kid))
2936 if (! return_hidden)
2937 retval(k++) = children(i);
2942 retval(k++) = children(i);
2946 retval.resize (k, 1);
2953base_properties::get_children (void) const
2955 return get_children_internal (false);
2959base_properties::get_hidden_children (void) const
2961 return get_children_internal (true);
2967 Matrix m (4, 4, 0.0);
2968 for (int i = 0; i < 4; i++)
2976 ColumnVector v (4, 0.0);
2982xform_vector (double x, double y, double z)
2984 ColumnVector v (4, 1.0);
2985 v(0) = x; v(1) = y; v(2) = z;
2990transform (const Matrix& m, double x, double y, double z)
2992 return (m * xform_vector (x, y, z));
2996xform_scale (double x, double y, double z)
2998 Matrix m (4, 4, 0.0);
2999 m(0,0) = x; m(1,1) = y; m(2,2) = z; m(3,3) = 1;
3004xform_translate (double x, double y, double z)
3006 Matrix m = xform_matrix ();
3007 m(0,3) = x; m(1,3) = y; m(2,3) = z; m(3,3) = 1;
3012scale (Matrix& m, double x, double y, double z)
3014 m = m * xform_scale (x, y, z);
3018translate (Matrix& m, double x, double y, double z)
3020 m = m * xform_translate (x, y, z);
3024xform (ColumnVector& v, const Matrix& m)
3030scale (ColumnVector& v, double x, double y, double z)
3038translate (ColumnVector& v, double x, double y, double z)
3046normalize (ColumnVector& v)
3048 double fact = 1.0/sqrt(v(0)*v(0)+v(1)*v(1)+v(2)*v(2));
3049 scale (v, fact, fact, fact);
3053dot (const ColumnVector& v1, const ColumnVector& v2)
3055 return (v1(0)*v2(0)+v1(1)*v2(1)+v1(2)*v2(2));
3059norm (const ColumnVector& v)
3061 return sqrt (dot (v, v));
3065cross (const ColumnVector& v1, const ColumnVector& v2)
3067 ColumnVector r = xform_vector ();
3068 r(0) = v1(1)*v2(2)-v1(2)*v2(1);
3069 r(1) = v1(2)*v2(0)-v1(0)*v2(2);
3070 r(2) = v1(0)*v2(1)-v1(1)*v2(0);
3077 static double data[32] = {
3087 memcpy (m.fortran_vec (), data, sizeof(double)*32);
3092cam2xform (const Array<double>& m)
3094 ColumnVector retval (4, 1.0);
3095 memcpy (retval.fortran_vec (), m.fortran_vec (), sizeof(double)*3);
3100xform2cam (const ColumnVector& v)
3102 return v.extract_n (0, 3).transpose ();
3106axes::properties::update_camera (void)
3108 double xd = (xdir_is ("normal") ? 1 : -1);
3109 double yd = (ydir_is ("normal") ? 1 : -1);
3110 double zd = (zdir_is ("normal") ? 1 : -1);
3112 Matrix xlimits = sx.scale (get_xlim ().matrix_value ());
3113 Matrix ylimits = sy.scale (get_ylim ().matrix_value ());
3114 Matrix zlimits = sz.scale (get_zlim ().matrix_value ());
3116 double xo = xlimits(xd > 0 ? 0 : 1);
3117 double yo = ylimits(yd > 0 ? 0 : 1);
3118 double zo = zlimits(zd > 0 ? 0 : 1);
3120 Matrix pb = get_plotboxaspectratio ().matrix_value ();
3122 bool autocam = (camerapositionmode_is ("auto")
3123 && cameratargetmode_is ("auto")
3124 && cameraupvectormode_is ("auto")
3125 && cameraviewanglemode_is ("auto"));
3126 bool dowarp = (autocam && dataaspectratiomode_is("auto")
3127 && plotboxaspectratiomode_is ("auto"));
3129 ColumnVector c_eye (xform_vector ());
3130 ColumnVector c_center (xform_vector ());
3131 ColumnVector c_upv (xform_vector ());
3133 if (cameratargetmode_is ("auto"))
3135 c_center(0) = (xlimits(0)+xlimits(1))/2;
3136 c_center(1) = (ylimits(0)+ylimits(1))/2;
3137 c_center(2) = (zlimits(0)+zlimits(1))/2;
3139 cameratarget = xform2cam (c_center);
3142 c_center = cam2xform (get_cameratarget ().matrix_value ());
3144 if (camerapositionmode_is ("auto"))
3146 Matrix tview = get_view ().matrix_value ();
3147 double az = tview(0), el = tview(1);
3148 double d = 5*sqrt(pb(0)*pb(0)+pb(1)*pb(1)+pb(2)*pb(2));
3150 if (el == 90 || el == -90)
3151 c_eye(2) = d*signum(el);
3156 c_eye(0) = d*cos(el)*sin(az);
3157 c_eye(1) = -d*cos(el)*cos(az);
3158 c_eye(2) = d*sin(el);
3160 c_eye(0) = c_eye(0)*(xlimits(1)-xlimits(0))/(xd*pb(0))+c_center(0);
3161 c_eye(1) = c_eye(1)*(ylimits(1)-ylimits(0))/(yd*pb(1))+c_center(1);
3162 c_eye(2) = c_eye(2)*(zlimits(1)-zlimits(0))/(zd*pb(2))+c_center(2);
3164 cameraposition = xform2cam (c_eye);
3167 c_eye = cam2xform (get_cameraposition ().matrix_value ());
3169 if (cameraupvectormode_is ("auto"))
3171 Matrix tview = get_view ().matrix_value ();
3172 double az = tview(0), el = tview(1);
3174 if (el == 90 || el == -90)
3176 c_upv(0) = -sin(az*M_PI/180.0)*(xlimits(1)-xlimits(0))/pb(0);
3177 c_upv(1) = cos(az*M_PI/180.0)*(ylimits(1)-ylimits(0))/pb(1);
3182 cameraupvector = xform2cam (c_upv);
3185 c_upv = cam2xform (get_cameraupvector ().matrix_value ());
3187 Matrix x_view = xform_matrix ();
3188 Matrix x_projection = xform_matrix ();
3189 Matrix x_viewport = xform_matrix ();
3190 Matrix x_normrender = xform_matrix ();
3191 Matrix x_pre = xform_matrix ();
3193 x_render = xform_matrix ();
3194 x_render_inv = xform_matrix ();
3196 scale (x_pre, pb(0), pb(1), pb(2));
3197 translate (x_pre, -0.5, -0.5, -0.5);
3198 scale (x_pre, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
3199 zd/(zlimits(1)-zlimits(0)));
3200 translate (x_pre, -xo, -yo, -zo);
3202 xform (c_eye, x_pre);
3203 xform (c_center, x_pre);
3204 scale (c_upv, pb(0)/(xlimits(1)-xlimits(0)), pb(1)/(ylimits(1)-ylimits(0)),
3205 pb(2)/(zlimits(1)-zlimits(0)));
3206 translate (c_center, -c_eye(0), -c_eye(1), -c_eye(2));
3208 ColumnVector F (c_center), f (F), UP (c_upv);
3212 if (std::abs (dot (f, UP)) > 1e-15)
3214 double fa = 1/sqrt(1-f(2)*f(2));
3215 scale (UP, fa, fa, fa);
3218 ColumnVector s = cross (f, UP);
3219 ColumnVector u = cross (s, f);
3221 scale (x_view, 1, 1, -1);
3222 Matrix l = xform_matrix ();
3223 l(0,0) = s(0); l(0,1) = s(1); l(0,2) = s(2);
3224 l(1,0) = u(0); l(1,1) = u(1); l(1,2) = u(2);
3225 l(2,0) = -f(0); l(2,1) = -f(1); l(2,2) = -f(2);
3226 x_view = x_view * l;
3227 translate (x_view, -c_eye(0), -c_eye(1), -c_eye(2));
3228 scale (x_view, pb(0), pb(1), pb(2));
3229 translate (x_view, -0.5, -0.5, -0.5);
3231 Matrix x_cube = x_view * unit_cube ();
3232 ColumnVector cmin = x_cube.row_min (), cmax = x_cube.row_max ();
3233 double xM = cmax(0)-cmin(0);
3234 double yM = cmax(1)-cmin(1);
3236 Matrix bb = get_boundingbox (true);
3240 if (cameraviewanglemode_is ("auto"))
3244 // FIXME -- was this really needed? When compared to Matlab, it
3245 // does not seem to be required. Need investigation with concrete
3246 // backend to see results visually.
3247 if (false && dowarp)
3248 af = 1.0 / (xM > yM ? xM : yM);
3251 if ((bb(2)/bb(3)) > (xM/yM))
3256 v_angle = 2 * (180.0 / M_PI) * atan (1 / (2 * af * norm (F)));
3258 cameraviewangle = v_angle;
3261 v_angle = get_cameraviewangle ();
3263 double pf = 1 / (2 * tan ((v_angle / 2) * M_PI / 180.0) * norm (F));
3264 scale (x_projection, pf, pf, 1);
3270 translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
3271 scale (x_viewport, bb(2)/xM, -bb(3)/yM, 1);
3278 if ((bb(2)/bb(3)) > (xM/yM))
3284 pix = (bb(2) < bb(3) ? bb(2) : bb(3));
3285 translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
3286 scale (x_viewport, pix, -pix, 1);
3289 x_normrender = x_viewport * x_projection * x_view;
3291 x_cube = x_normrender * unit_cube ();
3292 cmin = x_cube.row_min ();
3293 cmax = x_cube.row_max ();
3294 x_zlim.resize (1, 2);
3295 x_zlim(0) = cmin(2);
3296 x_zlim(1) = cmax(2);
3298 x_render = x_normrender;
3299 scale (x_render, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
3300 zd/(zlimits(1)-zlimits(0)));
3301 translate (x_render, -xo, -yo, -zo);
3303 x_viewtransform = x_view;
3304 x_projectiontransform = x_projection;
3305 x_viewporttransform = x_viewport;
3306 x_normrendertransform = x_normrender;
3307 x_rendertransform = x_render;
3309 x_render_inv = x_render.inverse ();
3311 // Note: these matrices are a slight modified version of the regular
3312 // matrices, more suited for OpenGL rendering (x_gl_mat1 => light
3315 scale (x_gl_mat1, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
3316 zd/(zlimits(1)-zlimits(0)));
3317 translate (x_gl_mat1, -xo, -yo, -zo);
3318 x_gl_mat2 = x_viewport * x_projection;
3322axes::properties::update_aspectratios (void)
3324 Matrix xlimits = get_xlim ().matrix_value ();
3325 Matrix ylimits = get_ylim ().matrix_value ();
3326 Matrix zlimits = get_zlim ().matrix_value ();
3328 double dx = (xlimits(1)-xlimits(0));
3329 double dy = (ylimits(1)-ylimits(0));
3330 double dz = (zlimits(1)-zlimits(0));
3332 if (dataaspectratiomode_is ("auto"))
3334 double dmin = xmin (xmin (dx, dy), dz);
3335 Matrix da (1, 3, 0.0);
3341 dataaspectratio = da;
3344 if (plotboxaspectratiomode_is ("auto"))
3346 if (dataaspectratiomode_is ("auto"))
3347 plotboxaspectratio = Matrix (1, 3, 1.0);
3350 Matrix da = get_dataaspectratio ().matrix_value ();
3351 Matrix pba (1, 3, 0.0);
3359 // FIXME -- if plotboxaspectratiomode is "manual", limits
3360 // and/or dataaspectratio might be adapted.
3363// The INTERNAL flag defines whether position or outerposition is used.
3366axes::properties::get_boundingbox (bool internal) const
3368 graphics_object obj = gh_manager::get_object (get_parent ());
3369 Matrix parent_bb = obj.get_properties ().get_boundingbox (true);
3370 Matrix pos = (internal ?
3371 get_position ().matrix_value ()
3372 : get_outerposition ().matrix_value ());
3375 pos = convert_position (pos, get_units (), "pixels",
3376 parent_bb.extract_n (0, 2, 1, 2));
3379 pos(1) = parent_bb(3) - pos(1) - pos(3);
3385graphics_xform::xform_vector (double x, double y, double z)
3387 return ::xform_vector (x, y, z);
3391graphics_xform::xform_eye (void)
3393 return ::xform_matrix ();
3397graphics_xform::transform (double x, double y, double z,
3398 bool use_scale) const
3407 return ::transform (xform, x, y, z);
3411graphics_xform::untransform (double x, double y, double z,
3412 bool use_scale) const
3414 ColumnVector v = ::transform (xform_inv, x, y, z);
3418 v(0) = sx.unscale (v(0));
3419 v(1) = sy.unscale (v(1));
3420 v(2) = sz.unscale (v(2));
3427axes::get_default (const caseless_str& name) const
3429 octave_value retval = default_properties.lookup (name);
3431 if (retval.is_undefined ())
3433 graphics_handle parent = get_parent ();
3434 graphics_object parent_obj = gh_manager::get_object (parent);
3436 retval = parent_obj.get_default (name);
3443// FIXME -- maybe this should go into array_property class?
3446check_limit_vals (double& min_val, double& max_val, double& min_pos,
3447 const array_property& data)
3449 double val = data.min_val ();
3450 if (! (xisinf (val) || xisnan (val)) && val < min_val)
3452 val = data.max_val ();
3453 if (! (xisinf (val) || xisnan (val)) && val > max_val)
3455 val = data.min_pos ();
3456 if (! (xisinf (val) || xisnan (val)) && val > 0 && val < min_pos)
3462check_limit_vals (double& min_val, double& max_val, double& min_pos,
3463 const octave_value& data)
3465 if (data.is_matrix_type ())
3467 Matrix m = data.matrix_value ();
3469 if (! error_state && m.numel () == 3)
3474 if (! (xisinf (val) || xisnan (val)) && val < min_val)
3478 if (! (xisinf (val) || xisnan (val)) && val > max_val)
3482 if (! (xisinf (val) || xisnan (val)) && val > 0 && val < min_pos)
3488// magform(x) Returns (a, b), where x = a * 10^b, a >= 1., and b is
3492magform (double x, double& a, int& b)
3501 double l = std::log10 (std::abs (x));
3502 double r = std::fmod (l, 1.);
3503 a = std::pow (10.0, r);
3504 b = static_cast<int> (l-r);
3516// A translation from Tom Holoryd's python code at
3517// http://kurage.nimh.nih.gov/tomh/tics.py
3518// FIXME -- add log ticks