changeset 18961:52e01aa1fe8b

Overhaul FLTK pan, rotate, zoom * graphics.in.h: add axes properties pan, rotate3d, mouse_wheel_zoom and custom set_pan which disables rotate3d. * graphics.cc: add custom set_rotate3d and link with pan property. Disable rotate3d for 2D plots. * __init_fltk__.cc: replace gui_mode and mouse_wheel_zoom with axes properties pan, rotate3d and mouse_wheel_zoom. Disable pan for legends, move them instead. * __add_default_menu__.m: Add new menu entries for new pan and zoom modes. * findall.m: Update test for added uimenus. Each axes now has its own properties for interactive GUI control of pan, rotate3d and mouse_wheel_zoom. Now it's possible to have several figures and set pan for the 2D plot in figure x and rotate3d for the 3D plot in figure y. There are two new pan modes: "Pan x only" and "Pan y only". The toolbar buttons "P" and "R" set pan and rotate3d for the last clicked axes object or the object below the center of the canvas if none was clicked yet. The legend can now be moved with the mouse.
author Andreas Weber <andy.weber.aw@gmail.com>
date Sun, 27 Jul 2014 22:31:14 +0200
parents 4c45986a278e
children d4b69559a0f7
files libinterp/corefcn/graphics.cc libinterp/corefcn/graphics.in.h libinterp/dldfcn/__init_fltk__.cc scripts/plot/util/findall.m scripts/plot/util/private/__add_default_menu__.m
diffstat 5 files changed, 141 insertions(+), 165 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/graphics.cc	Sat Jul 26 12:23:11 2014 +0200
+++ b/libinterp/corefcn/graphics.cc	Sun Jul 27 22:31:14 2014 +0200
@@ -6388,6 +6388,25 @@
 }
 
 void
+axes::properties::set_rotate3d (const octave_value& v)
+{
+  rotate3d.set (v, false, false);
+  if (rotate3d_is ("on"))
+    {
+      int ndim = calc_dimensions (gh_manager::get_object (get_parent ()));
+
+      // Disable rotate3d for 2D plots
+      if (ndim == 2)
+        {
+          rotate3d.set ("off", false, false);
+          pan.set ("on", false, false);
+        }
+      else
+       pan.set ("off", false, false);
+    }
+}
+
+void
 axes::properties::set_units (const octave_value& v)
 {
   if (! error_state)
@@ -7377,6 +7396,9 @@
     }
 
   xproperties.update_transform ();
+
+  // Disable rotate3d and select pan for 2D plots
+  xproperties.set_rotate3d (xproperties.get_rotate3d ());
 }
 
 inline
--- a/libinterp/corefcn/graphics.in.h	Sat Jul 26 12:23:11 2014 +0200
+++ b/libinterp/corefcn/graphics.in.h	Sun Jul 27 22:31:14 2014 +0200
@@ -3717,6 +3717,13 @@
 
     void delete_text_child (handle_property& h);
 
+    void set_pan (const octave_value& val)
+    {
+      pan.set (val, false, false);
+      if (pan_is ("on") || pan_is ("xon") || pan_is ("yon"))
+        rotate3d.set ("off", false, false);
+    }
+
     // See the genprops.awk script for an explanation of the
     // properties declarations.
     // Programming note: Keep property list sorted if new ones are added.
@@ -3757,12 +3764,15 @@
       any_property linestyleorder S , "-"
       double_property linewidth , 0.5
       radio_property minorgridlinestyle , "-|--|{:}|-.|none"
+      double_property mouse_wheel_zoom , 0.05
       radio_property nextplot , "add|replacechildren|{replace}"
       array_property outerposition u , default_axes_outerposition ()
+      radio_property pan s , "{on}|xon|yon|off"
       array_property plotboxaspectratio mu , Matrix (1, 3, 1.0)
       radio_property plotboxaspectratiomode u , "{auto}|manual"
       array_property position u , default_axes_position ()
       radio_property projection , "{orthographic}|perspective"
+      radio_property rotate3d S , "{off}|on"
       radio_property tickdir mu , "{in}|out"
       radio_property tickdirmode u , "{auto}|manual"
       array_property ticklength u , default_axes_ticklength ()
--- a/libinterp/dldfcn/__init_fltk__.cc	Sat Jul 26 12:23:11 2014 +0200
+++ b/libinterp/dldfcn/__init_fltk__.cc	Sun Jul 27 22:31:14 2014 +0200
@@ -131,6 +131,7 @@
 
   void print (const std::string& cmd, const std::string& term)
   {
+    //std::cout << "OpenGL_fltk::print(cmd=" << cmd << ", term=" << term << ") canvas size = " << w () << "x" << h () << std::endl;
 #ifdef HAVE_GL2PS_H
     FILE *fp;
     fp = octave_popen (cmd.c_str (), "w");
@@ -239,11 +240,6 @@
   }
 };
 
-// Parameter controlling how fast we zoom when using the scrool wheel.
-static double Vwheel_zoom_speed = 0.05;
-// Parameter controlling the GUI mode.
-static enum { pan_zoom, rotate_zoom, none } gui_mode;
-
 void script_cb (Fl_Widget*, void* data)
 {
   static_cast<uimenu::properties*> (data)->execute_callback ();
@@ -667,7 +663,7 @@
 public:
   plot_window (int xx, int yy, int ww, int hh, figure::properties& xfp)
     : Fl_Window (xx, yy, ww, hh + menu_h + status_h + 2, "octave"),
-      window_label (), shift (0), ndim (2), fp (xfp), canvas (0),
+      window_label (), shift (0), fp (xfp), canvas (0),
       autoscale (0), togglegrid (0), panzoom (0), rotate (0), help (0),
       status (0), resize_dummy (0), ax_obj (), pos_x (0), pos_y (0)
   {
@@ -712,10 +708,6 @@
     rotate->callback (button_callback, static_cast<void*> (this));
     rotate->tooltip ("Mouse Rotate");
 
-    // get dimensions, de-/activate rotate button, set gui_mode
-    gui_mode = rotate_zoom;
-    update_gui_mode ();
-
     help = new Fl_Button (4 * status_h, toolbar_y, status_h, status_h, "?");
     help->callback (button_callback, static_cast<void*> (this));
     help->tooltip ("Help");
@@ -909,19 +901,6 @@
     canvas->redraw ();
   }
 
-  void update_gui_mode (void)
-  {
-    ndim = calc_dimensions (gh_manager::get_object (fp.get___myhandle__ ()));
-
-    if (ndim == 3)
-      rotate->activate ();
-    else // ndim == 2
-      {
-        rotate->deactivate ();
-        gui_mode = pan_zoom;
-      }
-  }
-
   void set_name (void)
   {
     window_label = fp.get_title ();
@@ -943,9 +922,6 @@
   // Mod keys status
   int shift;
 
-  // Number of dimensions, 2 or 3.
-  int ndim;
-
   // Figure properties.
   figure::properties& fp;
 
@@ -978,15 +954,40 @@
       toggle_grid ();
 
     if (widg == panzoom)
-      gui_mode = pan_zoom;
+      set_on_ax_obj ("pan", "on");
 
-    if (widg == rotate && ndim == 3)
-      gui_mode = rotate_zoom;
+    if (widg == rotate)
+      set_on_ax_obj ("rotate3d", "on");
 
     if (widg == help)
       fl_message ("%s", help_text);
   }
 
+  void set_on_ax_obj (const std::string& name, const std::string& value)
+  {
+    // ax_obj is the last clicked axes object
+    if (ax_obj && ax_obj.isa ("axes"))
+      {
+        axes::properties& ap = dynamic_cast<axes::properties&>(ax_obj.get_properties ());
+        ap.set (name, value);
+      }
+    else // no axes object clicked so far
+      {
+        // take the object in the center of the canvas
+        graphics_handle gh = pixel2axes_or_ca (canvas->w () / 2, canvas->h () / 2);
+
+        if (gh.ok ())
+          {
+            graphics_object tmp = gh_manager::get_object (gh);
+            if (tmp.isa ("axes"))
+              {
+                axes::properties& ap = dynamic_cast<axes::properties&>(tmp.get_properties ());
+                ap.set (name, value);
+              }
+          }
+      }
+  }
+
   fltk_uimenu* uimenu;
   OpenGL_fltk* canvas;
   Fl_Button* autoscale;
@@ -1250,12 +1251,12 @@
 
                 case 'p':
                 case 'P':
-                  gui_mode = pan_zoom;
+                  set_on_ax_obj ("pan", "on");
                   break;
 
                 case 'r':
                 case 'R':
-                  gui_mode = rotate_zoom;
+                  set_on_ax_obj ("rotate3d", "on");
                   break;
                 }
             }
@@ -1288,7 +1289,7 @@
             pos_x = Fl::event_x ();
             pos_y = Fl::event_y () - menu_dy ();
 
-            set_currentpoint (Fl::event_x (), Fl::event_y () - menu_dy ());
+            set_currentpoint (pos_x, pos_y);
 
             gh = pixel2axes_or_ca (pos_x, pos_y);
 
@@ -1296,6 +1297,17 @@
               {
                 ax_obj = gh_manager::get_object (gh);
                 set_axes_currentpoint (ax_obj, pos_x, pos_y);
+
+                int ndim = calc_dimensions (ax_obj);
+
+                if (ndim == 3)
+                  rotate->activate ();
+                else // ndim == 2
+                  rotate->deactivate ();
+
+
+                fp.set_currentobject (ax_obj.get_handle ().value ());
+
               }
 
             fp.execute_windowbuttondownfcn (Fl::event_button());
@@ -1316,32 +1328,48 @@
               {
                 if (ax_obj && ax_obj.isa ("axes"))
                   {
-                    if (gui_mode == pan_zoom)
-                      pixel2status (ax_obj, pos_x, pos_y,
-                                    Fl::event_x (),
-                                    Fl::event_y () - menu_dy ());
-                    else
-                      view2status (ax_obj);
                     axes::properties& ap =
                       dynamic_cast<axes::properties&>
                       (ax_obj.get_properties ());
 
-                    double x0, y0, x1, y1;
-                    Matrix pos = fp.get_boundingbox (true);
-                    pixel2pos (ax_obj, pos_x, pos_y, x0, y0);
-                    pixel2pos (ax_obj, Fl::event_x (),
-                                       Fl::event_y () - menu_dy (),
-                                       x1, y1);
+                    // Don't pan or rotate legend
+                    if (ap.get_tag().compare ("legend") < 0)
+                      {
+                        if (ap.rotate3d_is ("on"))
+                          view2status (ax_obj);
+                        else
+                          pixel2status (ax_obj, pos_x, pos_y,
+                                        Fl::event_x (),
+                                        Fl::event_y () - menu_dy ());
+
+                        double x0, y0, x1, y1;
+                        Matrix pos = fp.get_boundingbox (true);
+                        pixel2pos (ax_obj, pos_x, pos_y, x0, y0);
+                        pixel2pos (ax_obj, Fl::event_x (),
+                                           Fl::event_y () - menu_dy (),
+                                           x1, y1);
 
-                    if (gui_mode == pan_zoom)
-                      ap.translate_view (x0, x1, y0, y1);
-                    else if (gui_mode == rotate_zoom)
-                      {
-                        double daz, del;
-                        daz = (Fl::event_x () - pos_x) / pos(2) * 360;
-                        del = (Fl::event_y () - menu_dy () - pos_y)
-                              / pos(3) * 360;
-                        ap.rotate_view (del, daz);
+                        if (ap.pan_is ("on"))
+                          ap.translate_view (x0, x1, y0, y1);
+                        else if (ap.pan_is ("xon"))
+                          ap.translate_view (x0, x1, y1, y1);
+                        else if (ap.pan_is ("yon"))
+                          ap.translate_view (x1, x1, y0, y1);
+                        else if (ap.rotate3d_is ("on"))
+                          {
+                            double daz, del;
+                            daz = (Fl::event_x () - pos_x) / pos(2) * 360;
+                            del = (Fl::event_y () - menu_dy () - pos_y)
+                                  / pos(3) * 360;
+                            ap.rotate_view (del, daz);
+                          }
+                      }
+                    else
+                      {  // move the position of the legend
+                        Matrix pos = ap.get_position ().matrix_value ();
+                        pos(0) += double (Fl::event_x () - pos_x) / canvas->w();
+                        pos(1) -= double (Fl::event_y () - menu_dy () - pos_y) / canvas->h();
+                        ap.set_position (pos);
                       }
 
                     pos_x = Fl::event_x ();
@@ -1377,6 +1405,9 @@
                   axes::properties& ap =
                     dynamic_cast<axes::properties&> (ax.get_properties ());
 
+                  // Parameter controlling how fast we zoom when using the scrool wheel.
+                  double Vwheel_zoom_speed = ap.get_mouse_wheel_zoom ();
+
                   // Determine if we're zooming in or out.
                   const double factor =
                     (Fl::event_dy () > 0) ? 1 / (1.0 - Vwheel_zoom_speed)
@@ -1707,7 +1738,6 @@
     if (win != windows.end ())
       {
         win->second->mark_modified ();
-        win->second->update_gui_mode ();
       }
   }
 
@@ -1962,6 +1992,7 @@
                   figure_manager::set_name (tmp);
                 }
                 break;
+
               case figure::properties::ID_POSITION:
                 {
                   std::string tmp = ov.string_value ();
@@ -2109,101 +2140,3 @@
 
   return retval;
 }
-
-// FIXME: This function should be abstracted and made potentially
-// available to all graphics toolkits.  This suggests putting it in
-// graphics.cc as is done for drawnow() and having the master
-// mouse_wheel_zoom function call fltk_mouse_wheel_zoom.  The same
-// should be done for gui_mode and fltk_gui_mode.  For now (2011.01.30),
-// just changing function names and docstrings.
-
-DEFUN_DLD (mouse_wheel_zoom, args, nargout,
-           "-*- texinfo -*-\n\
-@deftypefn  {Loadable Function} {@var{val} =} mouse_wheel_zoom ()\n\
-@deftypefnx {Loadable Function} {@var{old_val} =} mouse_wheel_zoom (@var{new_val})\n\
-@deftypefnx {Loadable Function} {} mouse_wheel_zoom (@var{new_val}, \"local\")\n\
-Query or set the mouse wheel zoom factor.\n\
-\n\
-The zoom factor is a number in the range (0,1) which is the percentage of the\n\
-current axis limits that will be used when zooming.  For example, if the\n\
-current x-axis limits are [0, 50] and @code{mouse_wheel_zoom} is 0.4 (40%),\n\
-then a zoom operation will change the limits by 20.\n\
-\n\
-When called from inside a function with the @qcode{\"local\"} option, the\n\
-variable is changed locally for the function and any subroutines it calls.  \n\
-The original variable value is restored when exiting the function.\n\
-\n\
-This function is currently implemented only for the FLTK graphics toolkit.\n\
-@seealso{gui_mode}\n\
-@end deftypefn")
-{
-#ifdef HAVE_FLTK
-  return SET_INTERNAL_VARIABLE_WITH_LIMITS(wheel_zoom_speed, 0.0001, 0.9999);
-#else
-  error ("mouse_wheel_zoom: not available without OpenGL and FLTK libraries");
-  return octave_value ();
-#endif
-}
-
-DEFUN_DLD (gui_mode, args, ,
-           "-*- texinfo -*-\n\
-@deftypefn  {Built-in Function} {@var{mode} =} gui_mode ()\n\
-@deftypefnx {Built-in Function} {} gui_mode (@var{mode})\n\
-Query or set the GUI mode for the current graphics toolkit.\n\
-The @var{mode} argument can be one of the following strings:\n\
-\n\
-@table @asis\n\
-@item @qcode{\"2d\"}\n\
-Allows panning and zooming of current axes.\n\
-\n\
-@item @qcode{\"3d\"}\n\
-Allows rotating and zooming of current axes.\n\
-\n\
-@item @qcode{\"none\"}\n\
-Mouse inputs have no effect.\n\
-@end table\n\
-\n\
-This function is currently implemented only for the FLTK graphics toolkit.\n\
-@seealso{mouse_wheel_zoom}\n\
-@end deftypefn")
-{
-#ifdef HAVE_FLTK
-  caseless_str mode_str;
-
-  if (gui_mode == pan_zoom)
-    mode_str = "2d";
-  else if (gui_mode == rotate_zoom)
-    mode_str = "3d";
-  else
-    mode_str = "none";
-
-  bool failed = false;
-
-  if (args.length () == 1)
-    {
-      if (args(0).is_string ())
-        {
-          mode_str = args(0).string_value ();
-
-          if (mode_str.compare ("2d"))
-            gui_mode = pan_zoom;
-          else if (mode_str.compare ("3d"))
-            gui_mode = rotate_zoom;
-          else if (mode_str.compare ("none"))
-            gui_mode = none;
-          else
-            failed = true;
-        }
-      else
-        failed = true;
-    }
-
-  if (failed)
-    error ("MODE must be one of the strings: \"2D\", \"3D\", or \"none\"");
-
-  return octave_value (mode_str);
-#else
-  error ("gui_mode: not available without OpenGL and FLTK libraries");
-  return octave_value ();
-#endif
-}
--- a/scripts/plot/util/findall.m	Sat Jul 26 12:23:11 2014 +0200
+++ b/scripts/plot/util/findall.m	Sun Jul 27 22:31:14 2014 +0200
@@ -56,7 +56,7 @@
 %! unwind_protect
 %!   h = findall (hf);
 %!   all_handles(1) = {"figure"};
-%!   all_handles(2:12,1) = {"uimenu"};
+%!   all_handles(2:15,1) = {"uimenu"};
 %!   assert (get (h, "type"), all_handles);
 %! unwind_protect_cleanup
 %!   close (hf);
--- a/scripts/plot/util/private/__add_default_menu__.m	Sat Jul 26 12:23:11 2014 +0200
+++ b/scripts/plot/util/private/__add_default_menu__.m	Sun Jul 27 22:31:14 2014 +0200
@@ -44,9 +44,12 @@
       uimenu (__e, "label", "&Grid", "callback", @grid_cb);
       uimenu (__e, "label", "Auto&scale", "callback", @autoscale_cb);
       gm = uimenu (__e, "label", "GUI &Mode");
-        uimenu (gm, "label", "Pan+Zoom", "callback", @guimode_cb);
-        uimenu (gm, "label", "Rotate+Zoom", "callback", @guimode_cb);
-        uimenu (gm, "label", "None+Zoom", "callback", @guimode_cb);
+        uimenu (gm, "label", "Pan x and y", "tag", "pan_on", "callback", @guimode_cb);
+        uimenu (gm, "label", "Pan x only", "tag", "pan_xon", "callback", @guimode_cb);
+        uimenu (gm, "label", "Pan y only", "tag", "pan_yon", "callback", @guimode_cb);
+        uimenu (gm, "label", "Rotate on", "tag", "rotate3d", "callback", @guimode_cb);
+        uimenu (gm, "label", "Enable mousezoom", "tag", "zoom_on", "callback", @guimode_cb);
+        uimenu (gm, "label", "Disable mousezoom", "tag", "zoom_off", "callback", @guimode_cb);
 
   endif
 
@@ -90,14 +93,22 @@
 endfunction
 
 function guimode_cb (h, e)
-  lbl = get (h, "label");
-  switch (lbl)
-    case "Pan+Zoom"
-      gui_mode ("2D");
-    case "Rotate+Zoom"
-      gui_mode ("3D");
-    case "None"
-      gui_mode ("None");
+  id = get (h, "tag");
+  switch (id)
+    case "pan_on"
+      set (gco, "pan", "on");
+    case "pan_xon"
+      set (gco, "pan", "xon");
+    case "pan_yon"
+      set (gco, "pan", "yon");
+    case "rotate3d"
+      set (gco, "rotate3d", "on");
+    case "no_pan_rotate"
+      set (gco, "pan", "off");
+      set (gco, "rotate3d", "off");
+    case "zoom_on"
+      set (gco, "mouse_wheel_zoom", 0.05);
+    case "zoom_off"
+      set (gco, "mouse_wheel_zoom", 0.0);
   endswitch
 endfunction
-