Better moving to the top left corner, and a menu presence.
[gliv.git] / src / matrix.c
blobbb6d255850a952cbb49cfebf676af19d08802af1
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 * See the COPYING file for license information.
18 * Guillaume Chazarain <guichaz@yahoo.fr>
21 /************************
22 * The MODELVIEW matrix *
23 ************************/
25 #include <string.h> /* memcpy() */
26 #include <GL/gl.h>
28 #include "gliv.h"
29 #include "matrix.h"
30 #include "math_floats.h" /* cosf(), sinf(), ... */
31 #include "options.h"
33 extern rt_struct *rt;
34 extern options_struct *options;
35 extern gliv_image *current_image;
38 * OpenGL uses a transposed matrix, we use a 'normal' one,
39 * we transpose it just before glLoadMatrix().
41 * Value: Index:
42 * c1 s1 0 x | 0 1 2 3
43 * s2 c2 0 y | 4 5 6 7
44 * 0 0 1 0 | 8 9 10 11 \ constant so
45 * 0 0 0 1 | 12 13 14 15 / unused.
48 #define MATRIX_C1 matrix[0]
49 #define MATRIX_S1 matrix[1]
50 #define MATRIX_X matrix[3]
51 #define MATRIX_S2 matrix[4]
52 #define MATRIX_C2 matrix[5]
53 #define MATRIX_Y matrix[7]
55 static gfloat matrix[8] = {
56 /* We only use two rows. */
57 1.0, 0.0, 0.0, 0.0,
58 0.0, 1.0, 0.0, 0.0
61 /* Used to know if the OpenGL matrix and this one are in sync. */
62 static gboolean matrix_changed = TRUE;
64 G_GNUC_PURE gfloat get_matrix_zoom(void)
67 * c1 == zoom*cos, s1 == zoom*sin
68 * cos^2 + sin^2 == 1 => c1^2 + s1^2 == zoom^2
70 return hypotf(MATRIX_C1, MATRIX_S1);
73 /* To be displayed in the status bar. */
74 G_GNUC_PURE gfloat get_matrix_angle(void)
76 gfloat cosine, angle;
78 cosine = MATRIX_C1 / get_matrix_zoom();
79 angle = acosf(cosine);
81 if (MATRIX_S1 < 0)
82 /* Negative sine => negative angle. */
83 angle *= -1.0;
85 return angle;
88 /* OpenGL coordinates to window coordinates. */
89 static void point_coord(gfloat x, gfloat y, gfloat * res_x, gfloat * res_y)
91 /* OpenGL coordinates through the modelview matrix. */
92 *res_x = MATRIX_C1 * x + MATRIX_S1 * y + MATRIX_X;
93 *res_y = MATRIX_S2 * x + MATRIX_C2 * y + MATRIX_Y;
95 /* And now through the projection matrix. */
96 *res_x += rt->wid_size->width / 2.0;
97 *res_y += rt->wid_size->height / 2.0;
100 static gfloat min4(gfloat a, gfloat b, gfloat c, gfloat d)
102 if (a > b)
103 a = b;
105 if (c > d)
106 c = d;
108 return MIN(a, c);
111 static gfloat max4(gfloat a, gfloat b, gfloat c, gfloat d)
113 if (a < b)
114 a = b;
116 if (c < d)
117 c = d;
119 return MAX(a, c);
122 /* Convenient function: a == b ? */
123 G_GNUC_PURE gboolean float_equal(gfloat a, gfloat b)
125 return fabsf(a - b) < 1e-3;
128 void get_matrix_bounding_box(gfloat * min_x, gfloat * max_x,
129 gfloat * min_y, gfloat * max_y)
131 gfloat x0, y0, x1, y1;
132 gfloat x2, y2, x3, y3;
133 gfloat half_w, half_h;
135 half_w = current_image->width / 2.0;
136 half_h = current_image->height / 2.0;
138 point_coord(-half_w, -half_h, &x0, &y0);
139 point_coord(half_w, -half_h, &x1, &y1);
140 point_coord(-half_w, half_h, &x2, &y2);
141 point_coord(half_w, half_h, &x3, &y3);
143 *min_x = min4(x0, x1, x2, x3);
144 *max_x = max4(x0, x1, x2, x3);
145 *min_y = min4(y0, y1, y2, y3);
146 *max_y = max4(y0, y1, y2, y3);
149 /*** Input, output. ***/
151 void write_gl_matrix(void)
153 /* *INDENT-OFF* */
154 static gfloat transposed[16] = {
155 1.0, 0.0, 0.0, 0.0,
156 0.0, 1.0, 0.0, 0.0,
157 0.0, 0.0, 1.0, 0.0,
158 0.0, 0.0, 0.0, 1.0
160 /* *INDENT-ON* */
162 if (matrix_changed) {
163 transposed[0] = MATRIX_C1;
164 transposed[5] = MATRIX_C2;
165 transposed[4] = MATRIX_S1;
166 transposed[1] = MATRIX_S2;
167 transposed[12] = MATRIX_X;
168 transposed[13] = MATRIX_Y;
170 glLoadMatrixf(transposed);
171 matrix_changed = FALSE;
175 /* In paramaters dest and src, NULL is the current matrix. */
176 void matrix_cpy(gfloat * dest, gfloat * src)
178 if (dest == NULL) {
179 dest = matrix;
180 matrix_changed = TRUE;
181 } else if (src == NULL)
182 src = matrix;
184 memcpy(dest, src, sizeof(matrix));
187 /*** Informations gathering about the matrix. ***/
189 G_GNUC_PURE static gboolean angle_is_right(void)
191 gfloat modulo;
193 modulo = fmodf(get_matrix_angle(), PI / 2.0);
195 return float_equal(modulo, 0.0);
198 gboolean matrix_tile_visible(tile_dim * tile)
200 static guint select_buffer[4];
202 glSelectBuffer(4, select_buffer);
203 glRenderMode(GL_SELECT);
205 glInitNames();
206 glPushName(0);
208 glRectf(tile->x0, tile->y0, tile->x1, tile->y1);
210 return glRenderMode(GL_RENDER) > 0;
213 G_GNUC_PURE gboolean is_matrix_symmetry(void)
216 * c1 == c2 => rotation, c1 == -c2 => symmetry.
217 * s1 == s2 => rotation, s1 == -s2 => symmetry.
219 * Since c1^2 + s1^2 == 1, we use c1 only if far enough from 0.
222 return (MATRIX_C1 < -0.5 || MATRIX_C1 > 0.5) ?
223 !float_equal(MATRIX_C1, MATRIX_C2) : !float_equal(MATRIX_S1, MATRIX_S2);
226 G_GNUC_PURE gboolean get_matrix_has_changed(void)
228 return matrix_changed;
231 G_GNUC_PURE gboolean is_filtering_needed(void)
233 return (float_equal(get_matrix_zoom(), 1.0) == FALSE) ||
234 (angle_is_right() == FALSE);
237 /*** Operations on the matrix. ***/
239 /* Returns FALSE if the image is already maximised. */
240 gboolean matrix_set_max_zoom(gint width, gint height, gboolean do_it)
242 gfloat min_x, max_x, min_y, max_y, zoom;
244 if (current_image == NULL)
245 return TRUE;
247 if (do_it == FALSE && (float_equal(MATRIX_X, 0.0) == FALSE ||
248 float_equal(MATRIX_Y, 0.0) == FALSE))
249 /* Image not centered. */
250 return TRUE;
252 if (width < 0)
253 width = rt->wid_size->width;
255 if (height < 0)
256 height = rt->wid_size->height;
258 if ((options->maximize == FALSE &&
259 (current_image->width < width && current_image->height < height)) ||
260 (options->scaledown == FALSE &&
261 (current_image->width > width || current_image->height > height)))
262 return TRUE;
264 get_matrix_bounding_box(&min_x, &max_x, &min_y, &max_y);
265 zoom = MIN(width / (max_x - min_x), height / (max_y - min_y));
267 if (float_equal(zoom, 1.0) == FALSE ||
268 float_equal(MATRIX_X, 0.0) == FALSE ||
269 float_equal(MATRIX_Y, 0.0) == FALSE) {
271 if (do_it) {
272 matrix_zoom(zoom, 0.0, 0.0);
273 MATRIX_X = MATRIX_Y = 0.0;
275 return TRUE;
278 return FALSE;
281 void matrix_reset(void)
283 gint i;
285 for (i = 0; i < 8; i++)
286 matrix[i] = (gfloat) (i % 5 == 0);
288 matrix_changed = TRUE;
292 * Rotation: Product:
293 * cos sin 0 0 | c1*cos+s2*sin s1*cos+c2*sin 0 x*cos+y*sin
294 * -sin cos 0 0 | -c1*sin+s2*cos -s1*sin+c2*cos 0 -x*sin+y*cos
295 * 0 0 1 0 | 0 0 1 0
296 * 0 0 0 1 | 0 0 0 1
298 void matrix_rotate(gfloat angle)
300 gfloat cosine, sine;
301 gfloat c1, s1, c2, s2, x, y;
302 gboolean zoom;
304 /* Do we maximize after rotating? */
305 zoom = (options->maximize || options->scaledown) &&
306 (matrix_set_max_zoom(-1, -1, FALSE) == FALSE);
308 cosine = cosf(angle);
309 sine = sinf(angle);
311 /* Backup, as we'll modify them. */
312 c1 = MATRIX_C1;
313 c2 = MATRIX_C2;
314 s1 = MATRIX_S1;
315 s2 = MATRIX_S2;
316 x = MATRIX_X;
317 y = MATRIX_Y;
319 MATRIX_C1 = c1 * cosine + s2 * sine;
320 MATRIX_S1 = s1 * cosine + c2 * sine;
321 MATRIX_S2 = -c1 * sine + s2 * cosine;
322 MATRIX_C2 = -s1 * sine + c2 * cosine;
324 MATRIX_X = x * cosine + y * sine;
325 MATRIX_Y = -x * sine + y * cosine;
327 if (zoom)
328 matrix_set_max_zoom(-1, -1, TRUE);
329 else
330 matrix_changed = TRUE;
333 void matrix_move(gfloat x, gfloat y)
335 MATRIX_X += x;
336 MATRIX_Y += y;
338 matrix_changed = TRUE;
341 /* (x, y): zoom center. */
342 void matrix_zoom(gfloat ratio, gfloat x, gfloat y)
344 gfloat offset_x, offset_y;
346 offset_x = rt->wid_size->width / 2.0 - x;
347 offset_y = rt->wid_size->height / 2.0 - y;
349 matrix_move(offset_x, offset_y);
351 MATRIX_C1 *= ratio;
352 MATRIX_S1 *= ratio;
353 MATRIX_X *= ratio;
354 MATRIX_S2 *= ratio;
355 MATRIX_C2 *= ratio;
356 MATRIX_Y *= ratio;
358 matrix_move(-offset_x, -offset_y);
361 static void flip(guchar id)
363 /* Flip either the x or y row. */
365 matrix[id] *= -1.0;
366 matrix[id + 1] *= -1.0;
367 /* matrix[id + 2] is 0.0. */
368 matrix[id + 3] *= -1.0;
370 matrix_changed = TRUE;
373 void matrix_flip_h(void)
375 /* Flip y. */
376 flip(4);
379 void matrix_flip_v(void)
381 /* Flip x. */
382 flip(0);
385 void matrix_set_top_left(void)
387 gfloat min_x, max_x, min_y, max_y;
389 get_matrix_bounding_box(&min_x, &max_x, &min_y, &max_y);
391 if (min_x < 0.0 || max_x > (gfloat) rt->wid_size->width)
392 matrix_move(-min_x, 0.0);
394 if (min_y < 0.0 || max_y > (gfloat) rt->wid_size->height)
395 matrix_move(0.0, -min_y);