From 9b11713580664fea7f3f80d89b9acd42195ca8c3 Mon Sep 17 00:00:00 2001 From: Don Pellegrino Date: Tue, 18 Aug 2009 06:10:41 +0000 Subject: Fixed bug in zooming and panning. Interactive testing of zooming by rubber band, zooming with the mouse wheel and panning all work with the window at any size, square or non-square. --- diff --git a/src/controller/actions/set_ortho.c b/src/controller/actions/set_ortho.c index 4309c76..ce66f60 100644 --- a/src/controller/actions/set_ortho.c +++ b/src/controller/actions/set_ortho.c @@ -7,15 +7,10 @@ void set_ortho (void) { - if (S.zoom.active) - { - gluOrtho2D (S.zoom.coords[0], - S.zoom.coords[1], S.zoom.coords[2], S.zoom.coords[3]); - } - else - { - gluOrtho2D (S.ortho.min_x, S.ortho.max_x, S.ortho.min_y, S.ortho.max_y); - } + gluOrtho2D (S.ortho.min_x, + S.ortho.max_x, + S.ortho.min_y, + S.ortho.max_y); return; } diff --git a/src/controller/actions/zoom.c b/src/controller/actions/zoom.c index b8e54cc..7209e85 100644 --- a/src/controller/actions/zoom.c +++ b/src/controller/actions/zoom.c @@ -11,52 +11,40 @@ #define S state0 void -zoom (int x1, int y1, int x2, int y2) +zoom (double x1, double y1, double x2, double y2) { + S.zoom.active = true; + + S.zoom.coords[0] = x1; + S.zoom.coords[1] = x2; + S.zoom.coords[2] = y1; + S.zoom.coords[3] = y2; + /* - * Convert the selection boundary from window coordinates to - * world coordinates. + * Preserve the aspect ratio. First convert the selection to the + * aspect ratio of the original geometry. The world is a square so + * convert the zoom region to a square. The reshape callback + * performs step two of converting the squarified zoom region to the + * aspect ratio of the window. This is done in reshape as the window + * may change while the zoom region is held the same. */ - glMatrixMode (GL_MODELVIEW); - glLoadIdentity (); - GLdouble model[16]; - glGetDoublev (GL_MODELVIEW_MATRIX, model); - GLdouble projection[16]; - glGetDoublev (GL_PROJECTION_MATRIX, projection); + double width = S.zoom.coords[1] - S.zoom.coords[0]; + double height = S.zoom.coords[3] - S.zoom.coords[2]; + double difference = fabs(width - height); + + if (width > height) + { + S.zoom.coords[2] -= 0.5 * difference; + S.zoom.coords[3] += 0.5 * difference; + } + else + { + S.zoom.coords[0] -= 0.5 * difference; + S.zoom.coords[1] += 0.5 * difference; + } + GLint viewport[4]; glGetIntegerv (GL_VIEWPORT, viewport); - - check_error (__FILE__, __LINE__); - - GLdouble start_position[3]; - gluUnProject (x1, - y1, - 0, - model, - projection, - viewport, - &start_position[0], - &start_position[1], &start_position[2]); - - check_error (__FILE__, __LINE__); - - GLdouble end_position[3]; - gluUnProject (x2, - y2, - 0, - model, - projection, - viewport, - &end_position[0], &end_position[1], &end_position[2]); - - check_error (__FILE__, __LINE__); - - S.zoom.active = true; - S.zoom.coords[0] = fmin (start_position[0], end_position[0]); - S.zoom.coords[1] = fmax (start_position[0], end_position[0]); - S.zoom.coords[2] = fmin (start_position[1], end_position[1]); - S.zoom.coords[3] = fmax (start_position[1], end_position[1]); - reshape (viewport[2], viewport[3]); glutPostRedisplay (); diff --git a/src/controller/actions/zoom.h b/src/controller/actions/zoom.h index bd5b852..4dc1769 100644 --- a/src/controller/actions/zoom.h +++ b/src/controller/actions/zoom.h @@ -2,8 +2,8 @@ #define ZOOM_H /* - * Perform a zoom operation. + * Perform a zoom operation. Inputs are in world coordinates. */ -void zoom (int x1, int y1, int x2, int y2); +void zoom (double x1, double y1, double x2, double y2); #endif // ZOOM_H diff --git a/src/controller/callbacks/display.c b/src/controller/callbacks/display.c index b9a76a6..bee3286 100644 --- a/src/controller/callbacks/display.c +++ b/src/controller/callbacks/display.c @@ -1,10 +1,16 @@ #include "display.h" #include "../../view/geometry.h" +#include "../actions/set_ortho.h" #include void display (void) { + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + set_ortho (); + + glMatrixMode (GL_MODELVIEW); glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); geometry (GL_RENDER); glutSwapBuffers (); diff --git a/src/controller/callbacks/keyboard.c b/src/controller/callbacks/keyboard.c index 9967ad3..9e667d8 100644 --- a/src/controller/callbacks/keyboard.c +++ b/src/controller/callbacks/keyboard.c @@ -4,6 +4,7 @@ #include "reshape.h" #include "../../view/state0.h" #include "../../model/state/pan_info_init.h" +#include "../../model/state/zoom_info_init.h" #include #define S state0 @@ -41,11 +42,11 @@ keyboard (unsigned char key, int x, int y) case 'r': /* - * Reset the view (unzoom). + * Reset the view (unzoom, recenter). */ pan_info_init (&S.pan); - S.zoom.active = false; - + zoom_info_init (&S.zoom); + GLint viewport[4]; glGetIntegerv (GL_VIEWPORT, viewport); reshape (viewport[2], viewport[3]); diff --git a/src/controller/callbacks/mouse.c b/src/controller/callbacks/mouse.c index f901476..34ff290 100644 --- a/src/controller/callbacks/mouse.c +++ b/src/controller/callbacks/mouse.c @@ -25,7 +25,7 @@ mouse (int button, int state, int x, int y) { // Deactive a panning event if one was happening. S.pan.active = false; - + if (S.selection.active && S.selection.purpose == ZOOM && glutGetModifiers () == GLUT_ACTIVE_CTRL) @@ -35,14 +35,49 @@ mouse (int button, int state, int x, int y) */ if (x == S.selection.x || y == S.selection.y) return; - + + /* + * Convert the selection boundary from window coordinates to + * world coordinates. + */ + glMatrixMode (GL_MODELVIEW); + glLoadIdentity (); + GLdouble model[16]; + glGetDoublev (GL_MODELVIEW_MATRIX, model); + GLdouble projection[16]; + glGetDoublev (GL_PROJECTION_MATRIX, projection); GLint viewport[4]; glGetIntegerv (GL_VIEWPORT, viewport); - - zoom (S.selection.x, - viewport[3] - S.selection.y, - x, - viewport[3] - y); + + check_error (__FILE__, __LINE__); + + GLdouble start_position[3]; + gluUnProject (x, + viewport[3] -y, + 0, + model, + projection, + viewport, + &start_position[0], + &start_position[1], &start_position[2]); + + check_error (__FILE__, __LINE__); + + GLdouble end_position[3]; + gluUnProject (S.selection.x, + viewport[3] - S.selection.y, + 0, + model, + projection, + viewport, + &end_position[0], &end_position[1], &end_position[2]); + + check_error (__FILE__, __LINE__); + + zoom (fmin (start_position[0], end_position[0]), + fmin (start_position[1], end_position[1]), + fmax (start_position[0], end_position[0]), + fmax (start_position[1], end_position[1])); } /* diff --git a/src/controller/callbacks/mouse_wheel.c b/src/controller/callbacks/mouse_wheel.c index 52d3c8b..9ca73bc 100644 --- a/src/controller/callbacks/mouse_wheel.c +++ b/src/controller/callbacks/mouse_wheel.c @@ -1,44 +1,74 @@ #include "mouse_wheel.h" #include "../actions/zoom.h" +#include "../../view/state0.h" #include +#include + +#define S state0 void mouse_wheel (int button, int dir, int x, int y) { /* - * Get the current coordinates, substract some fixed amount and - * then perform the zoom. + * Start at the origin of the window. An alternative would be to + * start at the position of the mouse. Calculate a bounding box + * that will be the selection and zoom to it. */ - GLint viewport[4]; - glGetIntegerv (GL_VIEWPORT, viewport); + double width = fabs(S.ortho.max_x - S.ortho.min_x); + double height = fabs(S.ortho.max_y - S.ortho.min_y); /* - * The step size could be either a fixed number of pixels or a percentage. + * Box the smaller of the two dimensions. */ - // int step = 5; - int step = (viewport[3] - viewport[1]) * 0.10; + double box = 0.0; + if (width <= height) + box = width; + else + box = height; /* - * Not that the focus of the zoom is currently the center of the - * window but could alternatively be the mouse pointer's position. + * The step size could be either a fixed number of pixels or a + * percentage. Here we take 10% of the size of the box. */ + double step = box * 0.10; + + double x1 = 0.0; + double y1 = 0.0; + double x2 = 0.0; + double y2 = 0.0; + + if (width < height) + { + x1 = S.ortho.min_x; + x2 = S.ortho.max_x; + y1 = ((S.ortho.max_y + S.ortho.min_y) / 2.0) - (0.5 * width); + y2 = ((S.ortho.max_y + S.ortho.min_y) / 2.0) + (0.5 * width); + } + else if (width > height) + { + x1 = ((S.ortho.max_x + S.ortho.min_x) / 2.0) - (0.5 * height); + x2 = ((S.ortho.max_x + S.ortho.min_x) / 2.0) + (0.5 * height); + y1 = S.ortho.min_y; + y2 = S.ortho.max_y; + } + else + { + x1 = S.ortho.min_x; + x2 = S.ortho.max_x; + y1 = S.ortho.min_y; + y2 = S.ortho.max_y; + } // Zoom in - if (dir > 0) + if (dir > 0) { - zoom (step, - step, - viewport[3] - step, - viewport[3] - step); + zoom (x1 + step, y1 + step, x2 - step, y2 - step); } // Zoom out else { - zoom (-step, - -step, - viewport[3] + step, - viewport[3] + step); + zoom (x1 - step, y1 - step, x2 + step, y2 + step); } return; diff --git a/src/controller/callbacks/reshape.c b/src/controller/callbacks/reshape.c index 2455ea3..fec5443 100644 --- a/src/controller/callbacks/reshape.c +++ b/src/controller/callbacks/reshape.c @@ -3,6 +3,8 @@ #include "../actions/set_ortho.h" #include "reshape.h" #include +#include +#include #define S state0 @@ -13,26 +15,60 @@ reshape (int w, int h) glLoadIdentity (); /* - * This scaling produces an odd effect when the coordinates are not - * centered at 0,0. When 0,0 is the lower left rather than the - * center this reshape is a bit unnatural since the image is not - * centered in the middle of the window. + * Scale the zoom region to the aspect ratio of the window. */ - - if (w <= h) + if (S.zoom.active) { - S.ortho.min_x = S.ortho_min; - S.ortho.max_x = S.ortho_max; - S.ortho.min_y = S.ortho_min * (double) h / (double) w; - S.ortho.max_y = S.ortho_max * (double) h / (double) w; + if (w >= h) + { + double scale + = ( ( (S.zoom.coords[1] - S.zoom.coords[0]) * ((double) w / (double) h) ) + - (S.zoom.coords[1] - S.zoom.coords[0]) ) + * 0.5; + + S.ortho.min_x = S.zoom.coords[0] - scale; + S.ortho.max_x = S.zoom.coords[1] + scale; + S.ortho.min_y = S.zoom.coords[2]; + S.ortho.max_y = S.zoom.coords[3]; + } + else + { + double scale + = ( ( (S.zoom.coords[3] - S.zoom.coords[2]) * ((double) h / (double) w) ) + - (S.zoom.coords[3] - S.zoom.coords[2]) ) + * 0.5; + + S.ortho.min_x = S.zoom.coords[0]; + S.ortho.max_x = S.zoom.coords[1]; + S.ortho.min_y = S.zoom.coords[2] - scale; + S.ortho.max_y = S.zoom.coords[3] + scale; + } } - else + + else { - S.ortho.min_x = S.ortho_min * (double) w / (double) h; - S.ortho.max_x = S.ortho_max * (double) w / (double) h; - S.ortho.min_y = S.ortho_min; - S.ortho.max_y = S.ortho_max; - } + /* + * This scaling produces an odd effect when the coordinates are + * not centered at 0,0. When 0,0 is the lower left rather than + * the center this reshape is a bit unnatural since the image is + * not centered in the middle of the window. Try chaning this + * to use the method used above for the zoom region scaling. + */ + if (w <= h) + { + S.ortho.min_x = S.ortho_min; + S.ortho.max_x = S.ortho_max; + S.ortho.min_y = S.ortho_min * (double) h / (double) w; + S.ortho.max_y = S.ortho_max * (double) h / (double) w; + } + else + { + S.ortho.min_x = S.ortho_min * (double) w / (double) h; + S.ortho.max_x = S.ortho_max * (double) w / (double) h; + S.ortho.min_y = S.ortho_min; + S.ortho.max_y = S.ortho_max; + } + } set_ortho (); diff --git a/src/model/geometry/density_legend_geometry.c b/src/model/geometry/density_legend_geometry.c index e5baa08..ac3280c 100644 --- a/src/model/geometry/density_legend_geometry.c +++ b/src/model/geometry/density_legend_geometry.c @@ -27,20 +27,10 @@ density_legend_geometry (void) const double *top; const double *bottom; - if (S.zoom.active) - { - left = &S.zoom.coords[0]; - right = &S.zoom.coords[1]; - bottom = &S.zoom.coords[2]; - top = &S.zoom.coords[3]; - } - else - { - left = &S.ortho.min_x; - right = &S.ortho.max_x; - bottom = &S.ortho.min_y; - top = &S.ortho.max_y; - } + left = &S.ortho.min_x; + right = &S.ortho.max_x; + bottom = &S.ortho.min_y; + top = &S.ortho.max_y; /* * This value should be a percentage of the world height so that it diff --git a/src/model/state/zoom_info.h b/src/model/state/zoom_info.h index 5916b3c..83fab2c 100644 --- a/src/model/state/zoom_info.h +++ b/src/model/state/zoom_info.h @@ -15,6 +15,7 @@ typedef struct /* * Left, right, bottom and top of zoom region in world coordinates. + * Rubber-band zoom. */ double coords[4]; } ZOOM_INFO; diff --git a/src/model/state/zoom_info_init.c b/src/model/state/zoom_info_init.c index f49dd59..aa230d8 100644 --- a/src/model/state/zoom_info_init.c +++ b/src/model/state/zoom_info_init.c @@ -4,6 +4,10 @@ void zoom_info_init (ZOOM_INFO * z) { z->active = false; - + z->coords[0] = 0.0; + z->coords[1] = 0.0; + z->coords[2] = 0.0; + z->coords[3] = 0.0; + return; } -- cgit v0.8.3.1-22-g547a