[AdgADim] Corrected arc behavior
[adg.git] / adg / adg-matrix.c
blob60e53b2e5ded09f39e5e2c6ed9b24f3a23981859
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009 Nicola Fontana <ntd at entidi.it>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
21 /**
22 * SECTION:adg-matrix
23 * @Section_Id:AdgMatrix
24 * @title: AdgMatrix
25 * @short_description: A wrapper for #cairo_matrix_t
27 * AdgMatrix is a wrapper in #GType syntax of the #cairo_matrix_t struct.
28 **/
30 /**
31 * AdgMatrix:
33 * Another name for #cairo_matrix_t: check its documentation for the
34 * fields description and visibility details.
35 **/
38 #include "adg-matrix.h"
40 #include <string.h>
41 #include <math.h>
44 GType
45 adg_matrix_get_type(void)
47 static int matrix_type = 0;
49 if (G_UNLIKELY(matrix_type == 0))
50 matrix_type = g_boxed_type_register_static("AdgMatrix",
51 (GBoxedCopyFunc) adg_matrix_dup,
52 g_free);
54 return matrix_type;
57 /**
58 * adg_matrix_identity:
60 * A constant identity matrix provided as facility.
62 * Returns: a pointer to the identity matrix
63 **/
64 const AdgMatrix *
65 adg_matrix_identity(void)
67 static AdgMatrix identity_matrix;
68 static gboolean initialized = FALSE;
70 if (G_UNLIKELY(!initialized)) {
71 cairo_matrix_init_identity(&identity_matrix);
72 initialized = TRUE;
75 return &identity_matrix;
78 /**
79 * adg_matrix_copy:
80 * @matrix: the destination #AdgMatrix
81 * @src: the source #AdgMatrix
83 * Copies @matrix to @dst.
85 * Returns: @matrix
86 **/
87 AdgMatrix *
88 adg_matrix_copy(AdgMatrix *matrix, const AdgMatrix *src)
90 g_return_val_if_fail(matrix != NULL, matrix);
91 g_return_val_if_fail(src != NULL, matrix);
93 memcpy(matrix, src, sizeof(AdgMatrix));
95 return matrix;
98 /**
99 * adg_matrix_dup:
100 * @matrix: the souce #AdgMatrix
102 * Duplicates @matrix.
104 * Returns: the duplicate of @matrix: must be freed with g_free()
105 * when no longer needed.
107 AdgMatrix *
108 adg_matrix_dup(const AdgMatrix *matrix)
110 g_return_val_if_fail(matrix != NULL, NULL);
112 return g_memdup(matrix, sizeof(AdgMatrix));
116 * adg_matrix_equal:
117 * @matrix1: an #AdgMatrix
118 * @matrix2: an #AdgMatrix
120 * Compares @matrix1 and @matrix2 and returns %TRUE if the matrices are equal.
122 * Returns: %TRUE if @matrix1 is equal to @matrix2, %FALSE otherwise
124 gboolean
125 adg_matrix_equal(const AdgMatrix *matrix1, const AdgMatrix *matrix2)
127 g_return_val_if_fail(matrix1 != NULL, FALSE);
128 g_return_val_if_fail(matrix2 != NULL, FALSE);
130 /* XXX: I don't know if the following is always correct */
131 return memcmp(matrix1, matrix2, sizeof(AdgMatrix)) == 0;
135 * adg_matrix_normalize:
136 * @matrix: the source/destination #AdgMatrix
138 * Gets rid of the scaling component of a matrix: considering the
139 * (0,0) and (1,1) points, this function normalizes @matrix by
140 * forcing a %M_SQRT2 distance (that is the distance these points
141 * have on the identity matrix). This is done by applying a common
142 * factor to the <varname>xx</varname> and <varname>yy</varname>
143 * components of @matrix, without considering translations.
145 * From the documentation of the
146 * <ulink url="http://cairographics.org/manual/cairo-matrix.html#cairo-matrix-t">cairo matrix</ulink>
147 * we have:
149 * |[
150 * x_new = xx * x + xy * y + x0;
151 * y_new = yx * x + yy * y + y0.
152 * ]|
154 * Calling P0 = (0, 0) and P1 = (1, 1), their coordinates in @matrix,
155 * applying an arbitrary factor (<varname>k</varname>) on the scaling
156 * component, are:
158 * |[
159 * P0.x = x0;
160 * P0.y = y0;
161 * P1.x = k * matrix->xx + matrix->xy + x0;
162 * P1.y = k * matrix->yx + matrix->yy + y0.
163 * ]|
165 * Translating @matrix of (-x0, -y0), as I don't care of the translation
166 * component (the original translation will be kept intact):
168 * |[
169 * P0.x = 0;
170 * P0.y = 0;
171 * P1.x = k * matrix->xx + matrix->xy;
172 * P1.y = k * matrix->yx + matrix->yy.
173 * ]|
175 * Let's force a distance of %2 on the sum of their squared difference,
176 * as Pythagoras suggests:
178 * |[
179 * (P1.x - P0.x)² + (P1.y - P0.y)² = 2.
180 * (k matrix->xx + matrix->xy)² + (k matrix->yy + matrix->yx)² = 2.
181 * k² matrix->xx² + matrix->xy² + 2 k matrix->xx matrix->xy +
182 * k² matrix->yy² + matrix->yx² + 2 k matrix->yy matrix->yx = 2.
183 * k² (matrix->xx² + matrix->yy²) +
184 * 2k (matrix->xx matrix->xy + matrix->yy matrix->yx) +
185 * matrix->xy² + matrix->yx² - 2 = 0.
186 * ]|
188 * Now we'll calculate the <varname>k</varname> factor by calling
189 * <varname>a</varname>, <varname>b</varname> and <varname>c</varname>
190 * the respective coefficients and using the second degree equation
191 * solver, taking only the greater root:
193 * |[
194 * a = matrix->xx² + matrix->yy²;
195 * b = 2 (matrix->xx matrix->xy + matrix->yy matrix->yx);
196 * c = matrix->xy² + matrix->yx² - 2;
197 * -b + √(b² - 4ac)
198 * k = ----------------.
199 * 2a
200 * ]|
202 * If the discriminant is less than %0 the negative square root is not
203 * possible with real numbers, so @matrix is left untouched and
204 * %FALSE is returned. Otherwise, once the <varname>k</varname> factor
205 * is found, it is applied to the original matrix and %TRUE is returned.
207 * As a bonus, in the quite common case the original matrix has a scaling
208 * ratio of %1 (that is matrix->xx == matrix->yy) and it is not rotated
209 * (matrix->xy and matrix->yx are %0), we have:
211 * |[
212 * // Shortcut for uniform scaled matrices not rotated:
213 * a = 2 matrix->xx²;
214 * b = 0;
215 * c = -2;
216 * -b + √(b² - 4ac) √(16 matrix->xx²) 1
217 * k = ---------------- = ----------------- = ----------.
218 * 2a 4 matrix->xx² matrix->xx
219 * ]|
221 * avoiding square root and a lot of multiplications.
223 * Returns: %TRUE on success, %FALSE on errors
225 gboolean
226 adg_matrix_normalize(AdgMatrix *matrix)
228 gdouble k;
230 g_return_val_if_fail(matrix != NULL, FALSE);
232 if (matrix->xx == matrix->yy && matrix->xy == 0 && matrix->yx == 0) {
233 /* Common situation: original matrix scaled with a
234 * ratio of 1 and not rotated */
235 k = 1. / matrix->xx;
236 } else {
237 gdouble a, b, c, d;
239 a = matrix->xx * matrix->xx + matrix->yy * matrix->yy;
240 b = (matrix->xx * matrix->xy + matrix->yy * matrix->yx) * 2;
241 c = matrix->xy * matrix->xy + matrix->yx * matrix->yx - 2;
243 /* Compute and check the discriminant */
244 d = b * b - a * c * 4;
245 if (d < 0)
246 return FALSE;
248 k = (sqrt(d) - b) / (a * 2);
251 /* Apply the normalization factor */
252 matrix->xx *= k;
253 matrix->yy *= k;
255 return TRUE;
259 * adg_matrix_transform:
260 * @matrix: the source/destination #AdgMatrix
261 * @transformation: the transformation to apply
262 * @mode: how @transformation should be applied
264 * Modifies @matrix applying @transformation in the way specified by
265 * @mode.
267 void
268 adg_matrix_transform(AdgMatrix *matrix, const AdgMatrix *transformation,
269 AdgTransformationMode mode)
271 AdgMatrix tmp_matrix;
273 g_return_if_fail(matrix != NULL);
274 g_return_if_fail(transformation != NULL);
276 switch (mode) {
277 case ADG_TRANSFORM_NONE:
278 break;
279 case ADG_TRANSFORM_BEFORE:
280 cairo_matrix_multiply(matrix, matrix, transformation);
281 break;
282 case ADG_TRANSFORM_AFTER:
283 cairo_matrix_multiply(matrix, transformation, matrix);
284 break;
285 case ADG_TRANSFORM_BEFORE_NORMALIZED:
286 adg_matrix_copy(&tmp_matrix, transformation);
287 adg_matrix_normalize(&tmp_matrix);
288 cairo_matrix_multiply(matrix, matrix, &tmp_matrix);
289 break;
290 case ADG_TRANSFORM_AFTER_NORMALIZED:
291 adg_matrix_copy(&tmp_matrix, transformation);
292 adg_matrix_normalize(&tmp_matrix);
293 cairo_matrix_multiply(matrix, &tmp_matrix, matrix);
294 break;