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() */
30 #include "math_floats.h" /* cosf(), sinf(), ... */
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().
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. */
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)
78 cosine
= MATRIX_C1
/ get_matrix_zoom();
79 angle
= acosf(cosine
);
82 /* Negative sine => negative 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
)
111 static gfloat
max4(gfloat a
, gfloat b
, gfloat c
, gfloat d
)
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)
154 static gfloat transposed
[16] = {
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
)
180 matrix_changed
= TRUE
;
181 } else if (src
== NULL
)
184 memcpy(dest
, src
, sizeof(matrix
));
187 /*** Informations gathering about the matrix. ***/
189 G_GNUC_PURE
static gboolean
angle_is_right(void)
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
);
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
)
247 if (do_it
== FALSE
&& (float_equal(MATRIX_X
, 0.0) == FALSE
||
248 float_equal(MATRIX_Y
, 0.0) == FALSE
))
249 /* Image not centered. */
253 width
= rt
->wid_size
->width
;
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
)))
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
) {
272 matrix_zoom(zoom
, 0.0, 0.0);
273 MATRIX_X
= MATRIX_Y
= 0.0;
281 void matrix_reset(void)
285 for (i
= 0; i
< 8; i
++)
286 matrix
[i
] = (gfloat
) (i
% 5 == 0);
288 matrix_changed
= TRUE
;
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
298 void matrix_rotate(gfloat angle
)
301 gfloat c1
, s1
, c2
, s2
, x
, y
;
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
);
311 /* Backup, as we'll modify them. */
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
;
328 matrix_set_max_zoom(-1, -1, TRUE
);
330 matrix_changed
= TRUE
;
333 void matrix_move(gfloat x
, gfloat 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
);
358 matrix_move(-offset_x
, -offset_y
);
361 static void flip(guchar id
)
363 /* Flip either the x or y row. */
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)
379 void matrix_flip_v(void)
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
);