Code

98a6cbf3b996d4fe13c923bc3ac3238831fcf13a
[inkscape.git] / src / svg / svg-affine.cpp
1 #define __SP_SVG_AFFINE_C__
3 /*
4  * SVG data parser
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   Raph Levien <raph@acm.org>
9  *
10  * Copyright (C) 1999-2002 Lauris Kaplinski
11  * Copyright (C) 1999 Raph Levien
12  *
13  * Released under GNU GPL, read the file 'COPYING' for more information
14  */
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
20 #include <cstring>
21 #include <string>
22 #include <cstdlib>
23 #include <cstdio>
24 #include <glib/gstrfuncs.h>
25 #include <libnr/nr-matrix-fns.h>
26 #include <libnr/nr-matrix-ops.h>
27 #include <libnr/nr-matrix-translate-ops.h>
28 #include <libnr/nr-rotate-fns.h>
29 #include <libnr/nr-rotate-matrix-ops.h>
30 #include <libnr/nr-scale-matrix-ops.h>
31 #include <libnr/nr-translate-matrix-ops.h>
32 #include <libnr/nr-translate-rotate-ops.h>
33 #include "svg.h"
34 #include "prefs-utils.h"
36 #ifndef M_PI
37 # define M_PI 3.14159265358979323846
38 #endif
40 bool
41 sp_svg_transform_read(gchar const *str, NR::Matrix *transform)
42 {
43         int idx;
44         char keyword[32];
45         double args[6];
46         int n_args;
47         size_t key_len;
49         if (str == NULL) return false;
51         NR::Matrix a(NR::identity());
53         idx = 0;
54         while (str[idx]) {
55                 /* skip initial whitespace */
56                 while (g_ascii_isspace (str[idx])) idx++;
58                 /* parse keyword */
59                 for (key_len = 0; key_len < sizeof (keyword); key_len++) {
60                         char c;
62                         c = str[idx];
63                         if (g_ascii_isalpha (c) || c == '-') {
64                                 keyword[key_len] = str[idx++];
65                         } else {
66                                 break;
67                         }
68                 }
69                 if (key_len >= sizeof (keyword)) return false;
70                 keyword[key_len] = '\0';
72                 /* skip whitespace */
73                 while (g_ascii_isspace (str[idx])) idx++;
75                 if (str[idx] != '(') return false;
76                 idx++;
78                 for (n_args = 0; ; n_args++) {
79                         char c;
80                         char *end_ptr;
82                         /* skip whitespace */
83                         while (g_ascii_isspace (str[idx])) idx++;
84                         c = str[idx];
85                         if (g_ascii_isdigit (c) || c == '+' || c == '-' || c == '.') {
86                                 if (n_args == sizeof (args) / sizeof (args[0])) return false; /* Too many args */
87                                 args[n_args] = g_ascii_strtod (str + idx, &end_ptr);
88                                 
89                                 //printf("took %d chars from '%s' to make %f\n",
90                                 //              end_ptr-(str+idx),
91                                 //              str+idx,
92                                 //              args[n_args]);
94                                 idx = end_ptr - (char *) str;
96                                 while (g_ascii_isspace (str[idx])) idx++;
98                                 /* skip optional comma */
99                                 if (str[idx] == ',') idx++;
100                         } else if (c == ')') {
101                                 break;
102                         } else {
103                                 return false;
104                         }
105                 }
106                 idx++;
108                 /* ok, have parsed keyword and args, now modify the transform */
109                 if (!strcmp (keyword, "matrix")) {
110                         if (n_args != 6) return false;
111                         a = NR_MATRIX_D_FROM_DOUBLE(args) * a;
112                 } else if (!strcmp (keyword, "translate")) {
113                         if (n_args == 1) {
114                                 args[1] = 0;
115                         } else if (n_args != 2) {
116                                 return false;
117                         }
118                         a = NR::translate(args[0], args[1]) * a;
119                 } else if (!strcmp (keyword, "scale")) {
120                         if (n_args == 1) {
121                                 args[1] = args[0];
122                         } else if (n_args != 2) {
123                                 return false;
124                         }
125                         a = NR::scale(args[0], args[1]) * a;
126                 } else if (!strcmp (keyword, "rotate")) {
127                         if (n_args != 1 && n_args != 3) {
128                                 return false;
129                         }
130                         NR::rotate const rot(rotate_degrees(args[0]));
131                         if (n_args == 3) {
132                                 a = ( NR::translate(-args[1], -args[2])
133                                       * rot
134                                       * NR::translate(args[1], args[2])
135                                       * a );
136                         } else {
137                                 a = rot * a;
138                         }
139                 } else if (!strcmp (keyword, "skewX")) {
140                         if (n_args != 1) return false;
141                         a = ( NR::Matrix(1, 0,
142                                          tan(args[0] * M_PI / 180.0), 1,
143                                          0, 0)
144                               * a );
145                 } else if (!strcmp (keyword, "skewY")) {
146                         if (n_args != 1) return false;
147                         a = ( NR::Matrix(1, tan(args[0] * M_PI / 180.0),
148                                          0, 1,
149                                          0, 0)
150                               * a );
151                 } else {
152                         return false; /* unknown keyword */
153                 }
154                 /* Skip trailing whitespace */
155              while (g_ascii_isspace (str[idx])) idx++;
156         }
158         *transform = a;
159         return true;
162 #define EQ(a,b) (fabs ((a) - (b)) < 1e-9)
164 gchar *
165 sp_svg_transform_write(NR::Matrix const &transform)
167         NRMatrix const t(transform);
168         return sp_svg_transform_write(&t);
171 gchar *
172 sp_svg_transform_write(NRMatrix const *transform)
174         double e;
176         if (!transform) {
177                 return NULL;
178         }
180         e = 0.000001 * NR_MATRIX_DF_EXPANSION (transform);
181         int prec = prefs_get_int_attribute("options.svgoutput", "numericprecision", 8);
182         int min_exp = prefs_get_int_attribute("options.svgoutput", "minimumexponent", -8);
184         /* fixme: We could use t1 * t1 + t2 * t2 here instead */
185         if (NR_DF_TEST_CLOSE (transform->c[1], 0.0, e) && NR_DF_TEST_CLOSE (transform->c[2], 0.0, e)) {
186                 if (NR_DF_TEST_CLOSE (transform->c[4], 0.0, e) && NR_DF_TEST_CLOSE (transform->c[5], 0.0, e)) {
187                         if (NR_DF_TEST_CLOSE (transform->c[0], 1.0, e) && NR_DF_TEST_CLOSE (transform->c[3], 1.0, e)) {
188                                 /* We are more or less identity */
189                                 return NULL;
190                         } else {
191                                 /* We are more or less scale */
192                                 gchar c[256];
193                                 unsigned p = 0;
194                                 strcpy (c + p, "scale(");
195                                 p += 6;
196                                 p += sp_svg_number_write_de (c + p, transform->c[0], prec, min_exp, FALSE);
197                                 c[p++] = ',';
198                                 p += sp_svg_number_write_de (c + p, transform->c[3], prec, min_exp, FALSE);
199                                 c[p++] = ')';
200                                 c[p] = '\000';
201                                 g_assert( p <= sizeof(c) );
202                                 return g_strdup(c);
203                         }
204                 } else {
205                         if (NR_DF_TEST_CLOSE (transform->c[0], 1.0, e) && NR_DF_TEST_CLOSE (transform->c[3], 1.0, e)) {
206                                 /* We are more or less translate */
207                                 gchar c[256];
208                                 unsigned p = 0;
209                                 strcpy (c + p, "translate(");
210                                 p += 10;
211                                 p += sp_svg_number_write_de (c + p, transform->c[4], prec, min_exp, FALSE);
212                                 c[p++] = ',';
213                                 p += sp_svg_number_write_de (c + p, transform->c[5], prec, min_exp, FALSE);
214                                 c[p++] = ')';
215                                 c[p] = '\000';
216                                 g_assert( p <= sizeof(c) );
217                                 return g_strdup(c);
218                         } else {
219                                 gchar c[256];
220                                 unsigned p = 0;
221                                 strcpy (c + p, "matrix(");
222                                 p += 7;
223                                 p += sp_svg_number_write_de (c + p, transform->c[0], prec, min_exp, FALSE);
224                                 c[p++] = ',';
225                                 p += sp_svg_number_write_de (c + p, transform->c[1], prec, min_exp, FALSE);
226                                 c[p++] = ',';
227                                 p += sp_svg_number_write_de (c + p, transform->c[2], prec, min_exp, FALSE);
228                                 c[p++] = ',';
229                                 p += sp_svg_number_write_de (c + p, transform->c[3], prec, min_exp, FALSE);
230                                 c[p++] = ',';
231                                 p += sp_svg_number_write_de (c + p, transform->c[4], prec, min_exp, FALSE);
232                                 c[p++] = ',';
233                                 p += sp_svg_number_write_de (c + p, transform->c[5], prec, min_exp, FALSE);
234                                 c[p++] = ')';
235                                 c[p] = '\000';
236                                 g_assert( p <= sizeof(c) );
237                                 return g_strdup(c);
238                         }
239                 }
240         } else {
241                 gchar c[256];
242                 unsigned p = 0;
243                 strcpy (c + p, "matrix(");
244                 p += 7;
245                 p += sp_svg_number_write_de (c + p, transform->c[0], prec, min_exp, FALSE);
246                 c[p++] = ',';
247                 p += sp_svg_number_write_de (c + p, transform->c[1], prec, min_exp, FALSE);
248                 c[p++] = ',';
249                 p += sp_svg_number_write_de (c + p, transform->c[2], prec, min_exp, FALSE);
250                 c[p++] = ',';
251                 p += sp_svg_number_write_de (c + p, transform->c[3], prec, min_exp, FALSE);
252                 c[p++] = ',';
253                 p += sp_svg_number_write_de (c + p, transform->c[4], prec, min_exp, FALSE);
254                 c[p++] = ',';
255                 p += sp_svg_number_write_de (c + p, transform->c[5], prec, min_exp, FALSE);
256                 c[p++] = ')';
257                 c[p] = '\000';
258                 g_assert( p <= sizeof(c) );
259                 return g_strdup(c);
260         }