# HG changeset patch # User shaia@mars.bioc # Date 1248378402 14400 # Node ID ee8a035f399741f1a772863c85f4cf7feef7a476 # Parent c58b8960c7d0bb02dc3177197e8e2ff8f546b51a imported patch fltk_zoom diff --git a/src/ChangeLog b/src/ChangeLog --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,20 @@ +2009-07-23 Shai Ayal + + * DLD-FUNCTIONS/fltk_backend.cc (plot_window::pixel2axes_or_ca): + Replace plot_window::pixel2axes and use algorithm from octavede. + (plot_window::handle): Zooming now works on the axes below the + mouse pointer instead of the current axes. Combine old and new + zooming methods. + (help_text): Update to reflect new mouse/key bindings. + +2009-07-23 Soren Hauberg + + * graphics.cc (axes::properties::zoom_about_point, + axes::properties::translate_view): New functions. + (axes::properties::zoom): make zoom_stack usage optional. + * DLD-FUNCTIONS/fltk_backend.cc (plot_window::handle): Use new + zoom API. + 2009-07-23 John W. Eaton * variables.cc (safe_symbol_lookup): New function. diff --git a/src/DLD-FUNCTIONS/fltk_backend.cc b/src/DLD-FUNCTIONS/fltk_backend.cc --- a/src/DLD-FUNCTIONS/fltk_backend.cc +++ b/src/DLD-FUNCTIONS/fltk_backend.cc @@ -70,11 +70,13 @@ g - toggle grid\n\ \n\ Mouse\n\ -left drag - zoom\n\ -right click - unzoom\n\ -double click - copy coordinates to clipboard\ +left drag - pan\n\ +mouse wheel - zoom\n\ +right drag - rectangle zoom\n\ +left double click - autoscale\n\ "; + class OpenGL_fltk : public Fl_Gl_Window { public: @@ -214,7 +216,7 @@ _h - status_h, status_h, status_h, - "H"); + "?"); help->callback (button_callback, static_cast (this)); status = new @@ -334,30 +336,34 @@ } } - graphics_handle pixel2axes (int /* px */, int /* py */) + graphics_handle pixel2axes_or_ca (int px, int py ) { Matrix kids = fp.get_children (); + int len = kids.length (); - for (octave_idx_type n = 0; n < kids.numel (); n++) + for (int k = 0; k < len; k++) { - graphics_object ax = gh_manager::get_object (kids (n)); - if (ax && ax.isa ("axes")) + graphics_handle hnd = gh_manager::lookup (kids(k)); + + if (hnd.ok ()) { -#if 0 - axes::properties& ap = - dynamic_cast (ax.get_properties ()); + graphics_object kid = gh_manager::get_object (hnd); + + if (kid.valid_object () && kid.isa ("axes")) + { + Matrix bb = kid.get_properties ().get_boundingbox (true); - // std::cout << "\npixpos="<= pixpos(0) && px <= pixpos(2) - && py >= pixpos(1) && py <= pixpos(3)) - return ap.get___myhandle__ (); -#endif + if (bb(0) <= px && px < (bb(0)+bb(2)) + && bb(1) <= py && py < (bb(1)+bb(3))) + { + return hnd; + } + } } } - - return graphics_handle (); + return fp.get_currentaxes (); } - + void pixel2status (int px0, int py0, int px1 = -1, int py1 = -1) { double x0, y0, x1, y1; @@ -399,7 +405,6 @@ int handle (int event) { static int px0,py0; - static graphics_handle h0 = graphics_handle (); int retval = Fl_Window::handle (event); @@ -429,11 +434,10 @@ break; case FL_PUSH: - if (Fl::event_button () == 1) + if (Fl::event_button () == 1 || Fl::event_button () == 3) { px0 = Fl::event_x (); py0 = Fl::event_y (); - h0 = pixel2axes (Fl::event_x (), Fl::event_y ()); return 1; } break; @@ -442,6 +446,26 @@ pixel2status (px0, py0, Fl::event_x (), Fl::event_y ()); if (Fl::event_button () == 1) { + graphics_object ax = + gh_manager::get_object (pixel2axes_or_ca (px0, py0)); + if (ax && ax.isa ("axes")) + { + axes::properties& ap = + dynamic_cast (ax.get_properties ()); + + double x0, y0, x1, y1; + pixel2pos (px0, py0, x0, y0); + pixel2pos (Fl::event_x (), Fl::event_y (), x1, y1); + px0 = Fl::event_x (); + py0 = Fl::event_y (); + + ap.translate_view (x0 - x1, y0 - y1); + mark_modified (); + } + return 1; + } + else if (Fl::event_button () == 3) + { canvas->zoom (true); Matrix zoom_box (1,4,0); zoom_box (0) = px0; @@ -450,20 +474,66 @@ zoom_box (3) = Fl::event_y (); canvas->set_zoom_box (zoom_box); canvas->redraw_overlay (); - return 1; } + break; + case FL_MOUSEWHEEL: + { + // Parameter controlling how fast we zoom. FIXME: Should + // this be user tweakable? + const double zoom_speed = 0.05; + + graphics_object ax = + gh_manager::get_object (pixel2axes_or_ca (Fl::event_x (), + Fl::event_y ())); + if (ax && ax.isa ("axes")) + { + axes::properties& ap = + dynamic_cast (ax.get_properties ()); + + // Determine if we're zooming in or out + const double factor = + (Fl::event_dy () > 0) ? 1.0 + zoom_speed : 1.0 - zoom_speed; + + // Get the point we're zooming about + double x1, y1; + pixel2pos (Fl::event_x (), Fl::event_y (), x1, y1); + + ap.zoom_about_point (x1, y1, factor, false); + mark_modified (); + } + } + return 1; + case FL_RELEASE: if (Fl::event_button () == 1) { + if ( Fl::event_clicks () == 1) + { + graphics_object ax = + gh_manager::get_object (pixel2axes_or_ca (Fl::event_x (), + Fl::event_y ())); + if (ax && ax.isa ("axes")) + { + axes::properties& ap = + dynamic_cast (ax.get_properties ()); + ap.set_xlimmode("auto"); + ap.set_ylimmode("auto"); + ap.set_zlimmode("auto"); + mark_modified (); + } + } + } + if (Fl::event_button () == 3) + { // end of drag -- zoom if (canvas->zoom ()) { canvas->zoom (false); double x0,y0,x1,y1; - graphics_object ax = - gh_manager::get_object (fp.get_currentaxes ()); + graphics_object ax = + gh_manager::get_object (pixel2axes_or_ca (px0, py0)); if (ax && ax.isa ("axes")) { axes::properties& ap = @@ -482,7 +552,6 @@ xl(0) = x1; xl(1) = x0; } - if (y0 < y1) { yl(0) = y0; @@ -497,26 +566,6 @@ mark_modified (); } } - // one click -- select axes - else if ( Fl::event_clicks () == 0) - { - std::cout << "ca="<< h0.value ()<<"\n"; - if (h0.ok ()) - fp.set_currentaxes (h0.value()); - return 1; - } - } - else if (Fl::event_button () == 3) - { - graphics_object ax = - gh_manager::get_object (fp.get_currentaxes ()); - if (ax && ax.isa ("axes")) - { - axes::properties& ap = - dynamic_cast (ax.get_properties ()); - ap.unzoom (); - mark_modified (); - } } break; } diff --git a/src/graphics.cc b/src/graphics.cc --- a/src/graphics.cc +++ b/src/graphics.cc @@ -3466,7 +3466,7 @@ labels = c; } -static void +void get_children_limits (double& min_val, double& max_val, double& min_pos, const Matrix& kids, char limit_type) { @@ -3704,14 +3704,63 @@ unwind_protect::run (); } +inline +double force_in_range (const double x, const double lower, const double upper) +{ + if (x < lower) + { return lower; } + else if (x > upper) + { return upper; } + else + { return x; } +} + void -axes::properties::zoom (const Matrix& xl, const Matrix& yl) -{ - zoom_stack.push_front (xlimmode.get ()); - zoom_stack.push_front (xlim.get ()); - zoom_stack.push_front (ylimmode.get ()); - zoom_stack.push_front (ylim.get ()); - +axes::properties::zoom_about_point (double x, double y, double factor, + bool push_to_zoom_stack) +{ + // FIXME: Do we need error checking here? + Matrix xlims = get_xlim ().matrix_value (); + Matrix ylims = get_ylim ().matrix_value (); + + // Get children axes limits + Matrix kids = get_children (); + double minx = octave_Inf; + double maxx = -octave_Inf; + double min_pos_x = octave_Inf; + get_children_limits (minx, maxx, min_pos_x, kids, 'x'); + + double miny = octave_Inf; + double maxy = -octave_Inf; + double min_pos_y = octave_Inf; + get_children_limits (miny, maxy, min_pos_y, kids, 'y'); + + // Perform the zooming + xlims (0) = x + factor * (xlims (0) - x); + xlims (1) = x + factor * (xlims (1) - x); + ylims (0) = y + factor * (ylims (0) - y); + ylims (1) = y + factor * (ylims (1) - y); + + // Make sure we stay within the range og the plot + xlims (0) = force_in_range (xlims (0), minx, maxx); + xlims (1) = force_in_range (xlims (1), minx, maxx); + ylims (0) = force_in_range (ylims (0), miny, maxy); + ylims (1) = force_in_range (ylims (1), miny, maxy); + + zoom (xlims, ylims, push_to_zoom_stack); +} + +void +axes::properties::zoom (const Matrix& xl, const Matrix& yl, bool push_to_zoom_stack) +{ + if (push_to_zoom_stack) + { + zoom_stack.push_front (xlimmode.get ()); + zoom_stack.push_front (xlim.get ()); + zoom_stack.push_front (ylimmode.get ()); + zoom_stack.push_front (ylim.get ()); + } + xlim = xl; xlimmode = "manual"; ylim = yl; @@ -3723,6 +3772,43 @@ } void +axes::properties::translate_view (double delta_x, double delta_y) +{ + // FIXME: Do we need error checking here? + Matrix xlims = get_xlim ().matrix_value (); + Matrix ylims = get_ylim ().matrix_value (); + + // Get children axes limits + Matrix kids = get_children (); + double minx = octave_Inf; + double maxx = -octave_Inf; + double min_pos_x = octave_Inf; + get_children_limits (minx, maxx, min_pos_x, kids, 'x'); + + double miny = octave_Inf; + double maxy = -octave_Inf; + double min_pos_y = octave_Inf; + get_children_limits (miny, maxy, min_pos_y, kids, 'y'); + + // Make sure we don't exceed the borders + if (delta_x > 0) + delta_x = std::min (xlims (1) + delta_x, maxx) - xlims (1); + else + delta_x = std::max (xlims (0) + delta_x, minx) - xlims (0); + xlims (0) = xlims (0) + delta_x; + xlims (1) = xlims (1) + delta_x; + + if (delta_y > 0) + delta_y = std::min (ylims (1) + delta_y, maxy) - ylims (1); + else + delta_y = std::max (ylims (0) + delta_y, miny) - ylims (0); + ylims (0) = ylims (0) + delta_y; + ylims (1) = ylims (1) + delta_y; + + zoom (xlims, ylims, false); +} + +void axes::properties::unzoom (void) { if (zoom_stack.size () >= 4) diff --git a/src/graphics.h.in b/src/graphics.h.in --- a/src/graphics.h.in +++ b/src/graphics.h.in @@ -2610,7 +2610,10 @@ ColumnVector coord2pixel (double x, double y, double z) const { return get_transform ().transform (x, y, z); } - void zoom (const Matrix& xl, const Matrix& yl); + void zoom_about_point (double x, double y, double factor, + bool push_to_zoom_stack = true); + void zoom (const Matrix& xl, const Matrix& yl, bool push_to_zoom_stack = true); + void translate_view (double delta_x, double delta_y); void unzoom (void); void clear_zoom_stack (void); @@ -3907,6 +3910,8 @@ void do_post_event (const graphics_event& e); }; +void get_children_limits (double& min_val, double& max_val, double& min_pos, + const Matrix& kids, char limit_type); // This function is NOT equivalent to the scripting language function gcf. OCTINTERP_API graphics_handle gcf (void);