-rw-r--r-- | src/controller/actions/set_ortho.c | 13 | ||||
-rw-r--r-- | src/controller/actions/zoom.c | 70 | ||||
-rw-r--r-- | src/controller/actions/zoom.h | 4 | ||||
-rw-r--r-- | src/controller/callbacks/display.c | 6 | ||||
-rw-r--r-- | src/controller/callbacks/keyboard.c | 7 | ||||
-rw-r--r-- | src/controller/callbacks/mouse.c | 49 | ||||
-rw-r--r-- | src/controller/callbacks/mouse_wheel.c | 66 | ||||
-rw-r--r-- | src/controller/callbacks/reshape.c | 68 | ||||
-rw-r--r-- | src/model/geometry/density_legend_geometry.c | 18 | ||||
-rw-r--r-- | src/model/state/zoom_info.h | 1 | ||||
-rw-r--r-- | src/model/state/zoom_info_init.c | 6 |
11 files changed, 197 insertions, 111 deletions
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 <GL/glut.h> 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 <GL/glut.h> #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 <GL/glut.h> +#include <math.h> + +#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 <GL/glut.h> +#include <math.h> +#include <stdio.h> #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; } |