gliv-1.7
[gliv.git] / src / matrix.c
bloba0ff7b86d1f090b40d80cd2e67eeeafaec9dcf4c
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 <gfc@altern.org>
21 /*************************
22 * The MODELVIEW matrix. *
23 *************************/
25 #include "gliv.h"
26 #include "math_floats.h" /* cosf(), sinf(), ... */
28 #include <string.h> /* memcpy() */
29 #include <GL/gl.h>
31 extern rt_struct *rt;
32 extern options_struct *options;
33 extern gliv_image *current_image;
36 * OpenGL uses a transposed matrix, we use a 'normal' one,
37 * we transpose it just before glLoadMatrix().
39 * Value: Index:
40 * c1 s1 0 x | 0 1 2 3
41 * s2 c2 0 y | 4 5 6 7
42 * 0 0 1 0 | 8 9 10 11 \ constant so
43 * 0 0 0 1 | 12 13 14 15 / unused.
46 #define MATRIX_C1 matrix[0]
47 #define MATRIX_S1 matrix[1]
48 #define MATRIX_X matrix[3]
49 #define MATRIX_S2 matrix[4]
50 #define MATRIX_C2 matrix[5]
51 #define MATRIX_Y matrix[7]
53 static gfloat matrix[8] = {
54 /* We only use two rows. */
55 1.0, 0.0, 0.0, 0.0,
56 0.0, 1.0, 0.0, 0.0
59 /* Used to know if the OpenGL matrix and this one are in sync. */
60 static gboolean matrix_changed = TRUE;
62 G_GNUC_PURE gfloat get_matrix_zoom(void)
65 * c1 == zoom*cos, s1 == zoom*sin
66 * cos^2 + sin^2 == 1 => c1^2 + s1^2 == zoom^2
68 return hypotf(MATRIX_C1, MATRIX_S1);
71 /* To be displayed in the status bar. */
72 G_GNUC_PURE gfloat get_matrix_angle(void)
74 gfloat cosine, angle;
76 cosine = MATRIX_C1 / get_matrix_zoom();
77 angle = acosf(cosine);
79 if (MATRIX_S1 < 0)
80 /* Negative sine => negative angle. */
81 angle *= -1.0;
83 return angle;
86 /* OpenGL coordinates to window coordinates. */
87 static void point_coord(gfloat x, gfloat y, gfloat * res_x, gfloat * res_y)
89 /* OpenGL coordinates through the modelview matrix. */
90 *res_x = MATRIX_C1 * x + MATRIX_S1 * y + MATRIX_X;
91 *res_y = MATRIX_S2 * x + MATRIX_C2 * y + MATRIX_Y;
93 /* And now through the projection matrix. */
94 *res_x += rt->wid_size->width / 2.0;
95 *res_y += rt->wid_size->height / 2.0;
98 static gfloat min4(gfloat a, gfloat b, gfloat c, gfloat d)
100 if (a > b)
101 a = b;
103 if (c > d)
104 c = d;
106 return MIN(a, c);
109 static gfloat max4(gfloat a, gfloat b, gfloat c, gfloat d)
111 if (a < b)
112 a = b;
114 if (c < d)
115 c = d;
117 return MAX(a, c);
120 /* Convenient function: a == b ? */
121 G_GNUC_PURE gboolean float_equal(gfloat a, gfloat b)
123 return fabsf(a - b) < 1e-3;
126 void get_matrix_bounding_box(gfloat * min_x, gfloat * max_x,
127 gfloat * min_y, gfloat * max_y)
129 gfloat x0, y0, x1, y1;
130 gfloat x2, y2, x3, y3;
131 gfloat half_w, half_h;
133 half_w = current_image->width / 2.0;
134 half_h = current_image->height / 2.0;
136 point_coord(-half_w, -half_h, &x0, &y0);
137 point_coord(half_w, -half_h, &x1, &y1);
138 point_coord(-half_w, half_h, &x2, &y2);
139 point_coord(half_w, half_h, &x3, &y3);
141 *min_x = min4(x0, x1, x2, x3);
142 *max_x = max4(x0, x1, x2, x3);
143 *min_y = min4(y0, y1, y2, y3);
144 *max_y = max4(y0, y1, y2, y3);
147 /*** Input, output. ***/
149 void write_gl_matrix(void)
151 /* *INDENT-OFF* */
152 static gfloat transposed[16] = {
153 1.0, 0.0, 0.0, 0.0,
154 0.0, 1.0, 0.0, 0.0,
155 0.0, 0.0, 1.0, 0.0,
156 0.0, 0.0, 0.0, 1.0
158 /* *INDENT-ON* */
160 if (matrix_changed) {
161 transposed[0] = MATRIX_C1;
162 transposed[5] = MATRIX_C2;
163 transposed[4] = MATRIX_S1;
164 transposed[1] = MATRIX_S2;
165 transposed[12] = MATRIX_X;
166 transposed[13] = MATRIX_Y;
168 glLoadMatrixf(transposed);
169 matrix_changed = FALSE;
173 /* In paramaters dest and src, NULL is the current matrix. */
174 void matrix_cpy(gfloat * dest, gfloat * src)
176 if (dest == NULL) {
177 dest = matrix;
178 matrix_changed = TRUE;
179 } else
180 /* src == NULL */
181 src = matrix;
183 memcpy(dest, src, sizeof(matrix));
186 /*** Informations gathering about the matrix. ***/
188 G_GNUC_PURE static gboolean angle_is_right(void)
190 gfloat modulo;
192 modulo = fmodf(get_matrix_angle(), PI / 2.0);
194 return float_equal(modulo, 0.0);
197 gboolean matrix_tile_visible(tile_dim * tile)
199 static guint select_buffer[4];
201 glSelectBuffer(4, select_buffer);
202 glRenderMode(GL_SELECT);
204 glInitNames();
205 glPushName(0);
207 glRectf(tile->x0, tile->y0, tile->x1, tile->y1);
209 return glRenderMode(GL_RENDER) > 0;
212 G_GNUC_PURE gboolean is_matrix_symmetry(void)
215 * c1 == c2 => rotation, c1 == -c2 => symmetry.
216 * s1 == s2 => rotation, s1 == -s2 => symmetry.
218 * Since c1^2 + s1^2 == 1, we use c1 only if far enough from 0.
221 return (MATRIX_C1 < -0.5 || MATRIX_C1 > 0.5) ?
222 (float_equal(MATRIX_C1, MATRIX_C2) == FALSE) :
223 (float_equal(MATRIX_S1, MATRIX_S2) == FALSE);
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);