Code

Merge from fe-moved
[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 <2geom/transforms.h>
28 #include <2geom/angle.h>
29 #include <libnr/nr-convert2geom.h>
30 #include "svg.h"
31 #include "preferences.h"
33 #ifndef M_PI
34 # define M_PI 3.14159265358979323846
35 #endif
37 bool
38 sp_svg_transform_read(gchar const *str, Geom::Matrix *transform)
39 {
40     int idx;
41     char keyword[32];
42     double args[6];
43     int n_args;
44     size_t key_len;
46     if (str == NULL) return false;
48     Geom::Matrix a(Geom::identity());
50     idx = 0;
51     while (str[idx]) {
52         /* skip initial whitespace */
53         while (g_ascii_isspace (str[idx])) idx++;
55         /* parse keyword */
56         for (key_len = 0; key_len < sizeof (keyword); key_len++) {
57             char c;
59             c = str[idx];
60             if (g_ascii_isalpha (c) || c == '-') {
61                 keyword[key_len] = str[idx++];
62             } else {
63                 break;
64             }
65         }
66         if (key_len >= sizeof (keyword)) return false;
67         keyword[key_len] = '\0';
69         /* skip whitespace */
70         while (g_ascii_isspace (str[idx])) idx++;
72         if (str[idx] != '(') return false;
73         idx++;
75         for (n_args = 0; ; n_args++) {
76             char c;
77             char *end_ptr;
79             /* skip whitespace */
80             while (g_ascii_isspace (str[idx])) idx++;
81             c = str[idx];
82             if (g_ascii_isdigit (c) || c == '+' || c == '-' || c == '.') {
83                 if (n_args == sizeof (args) / sizeof (args[0])) return false; /* Too many args */
84                 args[n_args] = g_ascii_strtod (str + idx, &end_ptr);
85                 
86                 //printf("took %d chars from '%s' to make %f\n",
87                 //              end_ptr-(str+idx),
88                 //              str+idx,
89                 //              args[n_args]);
91                 idx = end_ptr - (char *) str;
93                 while (g_ascii_isspace (str[idx])) idx++;
95                 /* skip optional comma */
96                 if (str[idx] == ',') idx++;
97             } else if (c == ')') {
98                 break;
99             } else {
100                 return false;
101             }
102         }
103         idx++;
105         /* ok, have parsed keyword and args, now modify the transform */
106         if (!strcmp (keyword, "matrix")) {
107             if (n_args != 6) return false;
108             a = (*((Geom::Matrix *) &(args)[0])) * a;
109         } else if (!strcmp (keyword, "translate")) {
110             if (n_args == 1) {
111                 args[1] = 0;
112             } else if (n_args != 2) {
113                 return false;
114             }
115             a = Geom::Translate(args[0], args[1]) * a;
116         } else if (!strcmp (keyword, "scale")) {
117             if (n_args == 1) {
118                 args[1] = args[0];
119             } else if (n_args != 2) {
120                 return false;
121             }
122             a = Geom::Scale(args[0], args[1]) * a;
123         } else if (!strcmp (keyword, "rotate")) {
124             if (n_args != 1 && n_args != 3) {
125                 return false;
126             }
127             Geom::Rotate const rot(Geom::deg_to_rad(args[0]));
128             if (n_args == 3) {
129                 a = ( Geom::Translate(-args[1], -args[2])
130                       * rot
131                       * Geom::Translate(args[1], args[2])
132                       * Geom::Matrix(a) );
133             } else {
134                 a = rot * a;
135             }
136         } else if (!strcmp (keyword, "skewX")) {
137             if (n_args != 1) return false;
138             a = ( Geom::Matrix(1, 0,
139                      tan(args[0] * M_PI / 180.0), 1,
140                      0, 0)
141                   * a );
142         } else if (!strcmp (keyword, "skewY")) {
143             if (n_args != 1) return false;
144             a = ( Geom::Matrix(1, tan(args[0] * M_PI / 180.0),
145                      0, 1,
146                      0, 0)
147                   * a );
148         } else {
149             return false; /* unknown keyword */
150         }
151         /* Skip trailing whitespace */
152              while (g_ascii_isspace (str[idx])) idx++;
153     }
155     *transform = a;
156     return true;
159 #define EQ(a,b) (fabs ((a) - (b)) < 1e-9)
161 gchar *
162 sp_svg_transform_write(Geom::Matrix const &transform)
164     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
166     double e = 0.000001 * transform.descrim();
167     int prec = prefs->getInt("/options/svgoutput/numericprecision", 8);
168     int min_exp = prefs->getInt("/options/svgoutput/minimumexponent", -8);
170     /* fixme: We could use t1 * t1 + t2 * t2 here instead */
171     if ( Geom::are_near(transform[1], 0.0, e) && Geom::are_near (transform[2], 0.0, e)) {
172         if (Geom::are_near (transform[4], 0.0, e) && Geom::are_near (transform[5], 0.0, e)) {
173             if (Geom::are_near (transform[0], 1.0, e) && Geom::are_near (transform[3], 1.0, e)) {
174                 /* We are more or less identity */
175                 return NULL;
176             } else {
177                 /* We are more or less scale */
178                 gchar c[256];
179                 unsigned p = 0;
180                 strcpy (c + p, "scale(");
181                 p += 6;
182                 p += sp_svg_number_write_de (c + p, transform[0], prec, min_exp);
183                 c[p++] = ',';
184                 p += sp_svg_number_write_de (c + p, transform[3], prec, min_exp);
185                 c[p++] = ')';
186                 c[p] = '\000';
187                 g_assert( p <= sizeof(c) );
188                 return g_strdup(c);
189             }
190         } else {
191             if (Geom::are_near (transform[0], 1.0, e) && Geom::are_near (transform[3], 1.0, e)) {
192                 /* We are more or less translate */
193                 gchar c[256];
194                 unsigned p = 0;
195                 strcpy (c + p, "translate(");
196                 p += 10;
197                 p += sp_svg_number_write_de (c + p, transform[4], prec, min_exp);
198                 c[p++] = ',';
199                 p += sp_svg_number_write_de (c + p, transform[5], prec, min_exp);
200                 c[p++] = ')';
201                 c[p] = '\000';
202                 g_assert( p <= sizeof(c) );
203                 return g_strdup(c);
204             } else {
205                 gchar c[256];
206                 unsigned p = 0;
207                 strcpy (c + p, "matrix(");
208                 p += 7;
209                 p += sp_svg_number_write_de (c + p, transform[0], prec, min_exp);
210                 c[p++] = ',';
211                 p += sp_svg_number_write_de (c + p, transform[1], prec, min_exp);
212                 c[p++] = ',';
213                 p += sp_svg_number_write_de (c + p, transform[2], prec, min_exp);
214                 c[p++] = ',';
215                 p += sp_svg_number_write_de (c + p, transform[3], prec, min_exp);
216                 c[p++] = ',';
217                 p += sp_svg_number_write_de (c + p, transform[4], prec, min_exp);
218                 c[p++] = ',';
219                 p += sp_svg_number_write_de (c + p, transform[5], prec, min_exp);
220                 c[p++] = ')';
221                 c[p] = '\000';
222                 g_assert( p <= sizeof(c) );
223                 return g_strdup(c);
224             }
225         }
226     } else {
227         gchar c[256];
228         unsigned p = 0;
229         strcpy (c + p, "matrix(");
230         p += 7;
231         p += sp_svg_number_write_de (c + p, transform[0], prec, min_exp);
232         c[p++] = ',';
233         p += sp_svg_number_write_de (c + p, transform[1], prec, min_exp);
234         c[p++] = ',';
235         p += sp_svg_number_write_de (c + p, transform[2], prec, min_exp);
236         c[p++] = ',';
237         p += sp_svg_number_write_de (c + p, transform[3], prec, min_exp);
238         c[p++] = ',';
239         p += sp_svg_number_write_de (c + p, transform[4], prec, min_exp);
240         c[p++] = ',';
241         p += sp_svg_number_write_de (c + p, transform[5], prec, min_exp);
242         c[p++] = ')';
243         c[p] = '\000';
244         g_assert( p <= sizeof(c) );
245         return g_strdup(c);
246     }
250 gchar *
251 sp_svg_transform_write(Geom::Matrix const *transform)
253     return sp_svg_transform_write(*transform);