Fixed a mess with the matrix.
[gliv.git] / src / matrix.c
blob78149851966fdd89e596b39535d01adc5dd2f5ab
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() */
27 #include "gliv.h"
28 #include "matrix.h"
29 #include "math_floats.h" /* cosf(), sinf(), ... */
30 #include "options.h"
31 #include "opengl.h"
32 #include "gliv-image.h"
34 extern rt_struct *rt;
35 extern options_struct *options;
36 extern GlivImage *current_image;
39 * OpenGL uses a transposed matrix, we use a 'normal' one,
40 * we transpose it just before glLoadMatrix().
42 * Value: Index:
43 * c1 s1 0 x | 0 1 2 3
44 * s2 c2 0 y | 4 5 6 7
45 * 0 0 1 0 | 8 9 10 11 \ constant so
46 * 0 0 0 1 | 12 13 14 15 / unused.
49 #define MATRIX_C1 matrix[0]
50 #define MATRIX_S1 matrix[1]
51 #define MATRIX_X matrix[3]
52 #define MATRIX_S2 matrix[4]
53 #define MATRIX_C2 matrix[5]
54 #define MATRIX_Y matrix[7]
56 static gfloat matrix[8] = {
57 /* We only use two rows. */
58 1.0, 0.0, 0.0, 0.0,
59 0.0, 1.0, 0.0, 0.0
62 /* Used to know if the OpenGL matrix and this one are in sync. */
63 static gboolean matrix_changed = TRUE;
65 G_GNUC_PURE gfloat get_matrix_zoom(void)
68 * c1 == zoom*cos, s1 == zoom*sin
69 * cos^2 + sin^2 == 1 => c1^2 + s1^2 == zoom^2
71 return hypotf(MATRIX_C1, MATRIX_S1);
74 /* To be displayed in the status bar. */
75 G_GNUC_PURE gfloat get_matrix_angle(void)
77 gfloat cosine, angle;
79 cosine = MATRIX_C1 / get_matrix_zoom();
80 angle = acosf(cosine);
82 if (MATRIX_S1 < 0)
83 /* Negative sine => negative angle. */
84 angle *= -1.0;
86 return angle;
89 /* OpenGL coordinates to window coordinates. */
90 static void point_coord(gfloat x, gfloat y, gfloat * res_x, gfloat * res_y)
92 /* OpenGL coordinates through the modelview matrix. */
93 *res_x = MATRIX_C1 * x + MATRIX_S1 * y + MATRIX_X;
94 *res_y = MATRIX_S2 * x + MATRIX_C2 * y + MATRIX_Y;
96 /* And now through the projection matrix. */
97 *res_x += rt->wid_size->width / 2.0;
98 *res_y += rt->wid_size->height / 2.0;
101 static gfloat min4(gfloat a, gfloat b, gfloat c, gfloat d)
103 if (a > b)
104 a = b;
106 if (c > d)
107 c = d;
109 return MIN(a, c);
112 static gfloat max4(gfloat a, gfloat b, gfloat c, gfloat d)
114 if (a < b)
115 a = b;
117 if (c < d)
118 c = d;
120 return MAX(a, c);
123 /* Convenient function: a == b ? */
124 G_GNUC_PURE gboolean float_equal(gfloat a, gfloat b)
126 return fabsf(a - b) < 1e-3;
129 void get_matrix_bounding_box(gfloat * min_x, gfloat * max_x,
130 gfloat * min_y, gfloat * max_y)
132 gfloat x0, y0, x1, y1;
133 gfloat x2, y2, x3, y3;
134 gfloat half_w, half_h;
136 half_w = current_image->width / 2.0;
137 half_h = current_image->height / 2.0;
139 point_coord(-half_w, -half_h, &x0, &y0);
140 point_coord(half_w, -half_h, &x1, &y1);
141 point_coord(-half_w, half_h, &x2, &y2);
142 point_coord(half_w, half_h, &x3, &y3);
144 *min_x = min4(x0, x1, x2, x3);
145 *max_x = max4(x0, x1, x2, x3);
146 *min_y = min4(y0, y1, y2, y3);
147 *max_y = max4(y0, y1, y2, y3);
150 /*** Input, output. ***/
152 void write_gl_matrix(void)
154 /* *INDENT-OFF* */
155 static gfloat transposed[16] = {
156 1.0, 0.0, 0.0, 0.0,
157 0.0, 1.0, 0.0, 0.0,
158 0.0, 0.0, 1.0, 0.0,
159 0.0, 0.0, 0.0, 1.0
161 /* *INDENT-ON* */
163 if (matrix_changed) {
164 transposed[0] = MATRIX_C1;
165 transposed[5] = MATRIX_C2;
166 transposed[4] = MATRIX_S1;
167 transposed[1] = MATRIX_S2;
168 transposed[12] = MATRIX_X;
169 transposed[13] = MATRIX_Y;
171 glLoadMatrixf(transposed);
172 matrix_changed = FALSE;
176 /* In parameters dest and src, NULL is the current matrix. */
177 void matrix_cpy(gfloat * dest, gfloat * src)
179 if (dest == NULL) {
180 dest = matrix;
181 matrix_changed = TRUE;
182 } else if (src == NULL)
183 src = matrix;
185 memcpy(dest, src, sizeof(matrix));
188 /*** Informations gathering about the matrix. ***/
190 G_GNUC_PURE static gboolean angle_is_right(void)
192 gfloat modulo;
194 modulo = fmodf(get_matrix_angle(), PI / 2.0);
196 return float_equal(modulo, 0.0);
199 gboolean matrix_tile_visible(tile_dim * tile)
201 static guint select_buffer[4];
203 glSelectBuffer(4, select_buffer);
204 glRenderMode(GL_SELECT);
206 glInitNames();
207 glPushName(0);
209 glRectf(tile->x0, tile->y0, tile->x1, tile->y1);
211 return glRenderMode(GL_RENDER) > 0;
214 G_GNUC_PURE gboolean is_matrix_symmetry(void)
217 * c1 == c2 => rotation, c1 == -c2 => symmetry.
218 * s1 == s2 => rotation, s1 == -s2 => symmetry.
220 * Since c1^2 + s1^2 == 1, we use c1 only if far enough from 0.
223 return (MATRIX_C1 < -0.5 || MATRIX_C1 > 0.5) ?
224 !float_equal(MATRIX_C1, MATRIX_C2) : !float_equal(MATRIX_S1, MATRIX_S2);
227 G_GNUC_PURE gboolean get_matrix_has_changed(void)
229 return matrix_changed;
232 G_GNUC_PURE gboolean is_filtering_needed(void)
234 return (float_equal(get_matrix_zoom(), 1.0) == FALSE) ||
235 (angle_is_right() == FALSE);
238 /*** Operations on the matrix. ***/
240 /* Returns FALSE if the image is already maximised. */
241 gboolean matrix_set_max_zoom(gint width, gint height, gboolean do_it)
243 gfloat min_x, max_x, min_y, max_y, zoom;
245 if (current_image == NULL)
246 return TRUE;
248 if (do_it == FALSE && (float_equal(MATRIX_X, 0.0) == FALSE ||
249 float_equal(MATRIX_Y, 0.0) == FALSE))
250 /* Image not centered. */
251 return TRUE;
253 if (width < 0)
254 width = rt->wid_size->width;
256 if (height < 0)
257 height = rt->wid_size->height;
259 if ((options->maximize == FALSE &&
260 (current_image->width < width && current_image->height < height)) ||
261 (options->scaledown == FALSE &&
262 (current_image->width > width || current_image->height > height)))
263 return TRUE;
265 get_matrix_bounding_box(&min_x, &max_x, &min_y, &max_y);
266 zoom = MIN(width / (max_x - min_x), height / (max_y - min_y));
268 if (float_equal(zoom, 1.0) == FALSE ||
269 float_equal(MATRIX_X, 0.0) == FALSE ||
270 float_equal(MATRIX_Y, 0.0) == FALSE) {
272 if (do_it) {
273 matrix_zoom(zoom, 0.0, 0.0);
274 MATRIX_X = MATRIX_Y = 0.0;
276 return TRUE;
279 return FALSE;
282 void matrix_reset(void)
284 gint i;
286 for (i = 0; i < 8; i++)
287 matrix[i] = (gfloat) (i % 5 == 0);
289 matrix_changed = TRUE;
293 * Rotation: Product:
294 * cos sin 0 0 | c1*cos+s2*sin s1*cos+c2*sin 0 x*cos+y*sin
295 * -sin cos 0 0 | -c1*sin+s2*cos -s1*sin+c2*cos 0 -x*sin+y*cos
296 * 0 0 1 0 | 0 0 1 0
297 * 0 0 0 1 | 0 0 0 1
299 void matrix_rotate(gfloat angle)
301 gfloat cosine, sine;
302 gfloat c1, s1, c2, s2, x, y;
303 gboolean zoom;
305 /* Do we maximize after rotating? */
306 zoom = (options->maximize || options->scaledown) &&
307 (matrix_set_max_zoom(-1, -1, FALSE) == FALSE);
309 cosine = cosf(angle);
310 sine = sinf(angle);
312 /* Backup, as we'll modify them. */
313 c1 = MATRIX_C1;
314 c2 = MATRIX_C2;
315 s1 = MATRIX_S1;
316 s2 = MATRIX_S2;
317 x = MATRIX_X;
318 y = MATRIX_Y;
320 MATRIX_C1 = c1 * cosine + s2 * sine;
321 MATRIX_S1 = s1 * cosine + c2 * sine;
322 MATRIX_S2 = -c1 * sine + s2 * cosine;
323 MATRIX_C2 = -s1 * sine + c2 * cosine;
325 MATRIX_X = x * cosine + y * sine;
326 MATRIX_Y = -x * sine + y * cosine;
328 if (zoom)
329 matrix_set_max_zoom(-1, -1, TRUE);
330 else
331 matrix_changed = TRUE;
334 void matrix_move(gfloat x, gfloat y)
336 MATRIX_X += x;
337 MATRIX_Y += y;
339 matrix_changed = TRUE;
342 /* (x, y): zoom center. */
343 void matrix_zoom(gfloat ratio, gfloat x, gfloat y)
345 gfloat offset_x, offset_y;
347 offset_x = rt->wid_size->width / 2.0 - x;
348 offset_y = rt->wid_size->height / 2.0 - y;
350 matrix_move(offset_x, offset_y);
352 MATRIX_C1 *= ratio;
353 MATRIX_S1 *= ratio;
354 MATRIX_X *= ratio;
355 MATRIX_S2 *= ratio;
356 MATRIX_C2 *= ratio;
357 MATRIX_Y *= ratio;
359 matrix_move(-offset_x, -offset_y);
362 static void flip(guchar id)
364 /* Flip either the x or y row. */
366 matrix[id] *= -1.0;
367 matrix[id + 1] *= -1.0;
368 /* matrix[id + 2] is 0.0. */
369 matrix[id + 3] *= -1.0;
371 matrix_changed = TRUE;
374 void matrix_flip_h(void)
376 /* Flip y. */
377 flip(4);
380 void matrix_flip_v(void)
382 /* Flip x. */
383 flip(0);
386 void matrix_set_top_left(void)
388 gfloat min_x, max_x, min_y, max_y;
390 get_matrix_bounding_box(&min_x, &max_x, &min_y, &max_y);
392 if (min_x < 0.0 || max_x > (gfloat) rt->wid_size->width)
393 matrix_move(-min_x, 0.0);
395 if (min_y < 0.0 || max_y > (gfloat) rt->wid_size->height)
396 matrix_move(0.0, -min_y);
399 void configure_matrix(GlivImage * im)
401 GlivImage *old = current_image;
403 current_image = im;
404 matrix_reset();
405 if (options->maximize || options->scaledown)
406 matrix_set_max_zoom(-1, -1, TRUE);
408 else if (options->no_center)
409 matrix_set_top_left();
411 current_image = old;
414 gfloat *get_matrix_for_image(GlivImage * im)
416 gfloat current_matrix[8];
417 gfloat *new_matrix;
419 matrix_cpy(current_matrix, NULL);
420 configure_matrix(im);
422 new_matrix = g_new(gfloat, 8);
423 matrix_cpy(new_matrix, NULL);
425 matrix_cpy(NULL, current_matrix);
427 return new_matrix;