Code

Merge from trunk.
[inkscape.git] / src / svg / svg-affine-test.h
1 #include <cxxtest/TestSuite.h>
3 #include "svg/svg.h"
4 #include "streq.h"
5 #include <2geom/matrix.h>
6 #include <algorithm>
7 #include <glib.h>
8 #include <iostream>
9 #include <math.h>
10 #include <utility>
12 class SvgAffineTest : public CxxTest::TestSuite
13 {
14 private:
15     struct test_t {
16         char const * str;
17         Geom::Matrix matrix;
18     };
19     struct approx_equal_pred {
20         bool operator()(Geom::Matrix const &ref, Geom::Matrix const &cm) const
21         {
22             double maxabsdiff = 0;
23             for(size_t i=0; i<6; i++) {
24                 maxabsdiff = std::max(std::abs(ref[i]-cm[i]), maxabsdiff);
25             }
26             return maxabsdiff < 1e-14;
27         }
28     };
29     static test_t const read_matrix_tests[3];
30     static test_t const read_translate_tests[3];
31     static test_t const read_scale_tests[3];
32     static test_t const read_rotate_tests[4];
33     static test_t const read_skew_tests[3];
34     static char const * const read_fail_tests[25];
35     static test_t const write_matrix_tests[2];
36     static test_t const write_translate_tests[3];
37     static test_t const write_scale_tests[2];
38     static test_t const write_rotate_tests[2];
39     static test_t const write_skew_tests[3];
40 public:
41     SvgAffineTest() {
42     }
44 // createSuite and destroySuite get us per-suite setup and teardown
45 // without us having to worry about static initialization order, etc.
46     static SvgAffineTest *createSuite() { return new SvgAffineTest(); }
47     static void destroySuite( SvgAffineTest *suite ) { delete suite; }
49     void testReadIdentity()
50     {
51         char const* strs[] = {
52             //0,
53             "",
54             "matrix(1,0,0,1,0,0)",
55             "translate(0,0)",
56             "scale(1,1)",
57             "rotate(0,0,0)",
58             "skewX(0)",
59             "skewY(0)"};
60         size_t n = G_N_ELEMENTS(strs);
61         for(size_t i=0; i<n; i++) {
62             Geom::Matrix cm;
63             TSM_ASSERT(strs[i] , sp_svg_transform_read(strs[i], &cm));
64             TSM_ASSERT_EQUALS(strs[i] , Geom::identity() , cm);
65         }
66     }
68     void testWriteIdentity()
69     {
70         TS_ASSERT_EQUALS(sp_svg_transform_write(Geom::identity()) , (void*)0)
71     }
73     void testReadMatrix()
74     {
75         for(size_t i=0; i<G_N_ELEMENTS(read_matrix_tests); i++) {
76             Geom::Matrix cm;
77             TSM_ASSERT(read_matrix_tests[i].str , sp_svg_transform_read(read_matrix_tests[i].str, &cm));
78             TSM_ASSERT_RELATION(read_matrix_tests[i].str , approx_equal_pred , read_matrix_tests[i].matrix , cm);
79         }
80     }
82     void testReadTranslate()
83     {
84         for(size_t i=0; i<G_N_ELEMENTS(read_translate_tests); i++) {
85             Geom::Matrix cm;
86             TSM_ASSERT(read_translate_tests[i].str , sp_svg_transform_read(read_translate_tests[i].str, &cm));
87             TSM_ASSERT_RELATION(read_translate_tests[i].str , approx_equal_pred , read_translate_tests[i].matrix , cm);
88         }
89     }
91     void testReadScale()
92     {
93         for(size_t i=0; i<G_N_ELEMENTS(read_scale_tests); i++) {
94             Geom::Matrix cm;
95             TSM_ASSERT(read_scale_tests[i].str , sp_svg_transform_read(read_scale_tests[i].str, &cm));
96             TSM_ASSERT_RELATION(read_scale_tests[i].str , approx_equal_pred , read_scale_tests[i].matrix , cm);
97         }
98     }
100     void testReadRotate()
101     {
102         for(size_t i=0; i<G_N_ELEMENTS(read_rotate_tests); i++) {
103             Geom::Matrix cm;
104             TSM_ASSERT(read_rotate_tests[i].str , sp_svg_transform_read(read_rotate_tests[i].str, &cm));
105             TSM_ASSERT_RELATION(read_rotate_tests[i].str , approx_equal_pred , read_rotate_tests[i].matrix , cm);
106         }
107     }
109     void testReadSkew()
110     {
111         for(size_t i=0; i<G_N_ELEMENTS(read_skew_tests); i++) {
112             Geom::Matrix cm;
113             TSM_ASSERT(read_skew_tests[i].str , sp_svg_transform_read(read_skew_tests[i].str, &cm));
114             TSM_ASSERT_RELATION(read_skew_tests[i].str , approx_equal_pred , read_skew_tests[i].matrix , cm);
115         }
116     }
118     void testWriteMatrix()
119     {
120         for(size_t i=0; i<G_N_ELEMENTS(write_matrix_tests); i++) {
121             char * str = sp_svg_transform_write(write_matrix_tests[i].matrix);
122             TS_ASSERT_RELATION(streq_rel , str , write_matrix_tests[i].str);
123             g_free(str);
124         }
125     }
127     void testWriteTranslate()
128     {
129         for(size_t i=0; i<G_N_ELEMENTS(write_translate_tests); i++) {
130             char * str = sp_svg_transform_write(write_translate_tests[i].matrix);
131             TS_ASSERT_RELATION(streq_rel , str , write_translate_tests[i].str);
132             g_free(str);
133         }
134     }
136     void testWriteScale()
137     {
138         for(size_t i=0; i<G_N_ELEMENTS(write_scale_tests); i++) {
139             char * str = sp_svg_transform_write(write_scale_tests[i].matrix);
140             TS_ASSERT_RELATION(streq_rel , str , write_scale_tests[i].str);
141             g_free(str);
142         }
143     }
145     void testWriteRotate()
146     {
147         for(size_t i=0; i<G_N_ELEMENTS(write_rotate_tests); i++) {
148             char * str = sp_svg_transform_write(write_rotate_tests[i].matrix);
149             TS_ASSERT_RELATION(streq_rel , str , write_rotate_tests[i].str);
150             g_free(str);
151         }
152     }
154     void testWriteSkew()
155     {
156         for(size_t i=0; i<G_N_ELEMENTS(write_skew_tests); i++) {
157             char * str = sp_svg_transform_write(write_skew_tests[i].matrix);
158             TS_ASSERT_RELATION(streq_rel , str , write_skew_tests[i].str);
159             g_free(str);
160         }
161     }
163     void testReadConcatenation()
164     {
165         // NOTE: According to the SVG specification (see the syntax at http://www.w3.org/TR/SVG/coords.html#TransformAttribute
166         //       there should be 1 or more comma-wsp sequences between transforms... This doesn't make sense and it seems
167         //       likely that instead of a + they meant a ? (zero or one comma-wsp sequences).
168         char const * str = "skewY(17)skewX(9)translate(7,13)scale(2)rotate(13)translate(3,5)";
169         Geom::Matrix ref(2.0199976232558053, 1.0674773585906016, -0.14125199392774669, 1.9055550612095459, 14.412730624347654, 28.499820929377454); // Precomputed using Mathematica
170         Geom::Matrix cm;
171         TS_ASSERT(sp_svg_transform_read(str, &cm));
172         TS_ASSERT_RELATION(approx_equal_pred , ref , cm);
173     }
175     void testReadFailures()
176     {
177         for(size_t i=0; i<G_N_ELEMENTS(read_fail_tests); i++) {
178             Geom::Matrix cm;
179             TSM_ASSERT(read_fail_tests[i] , !sp_svg_transform_read(read_fail_tests[i], &cm));
180         }
181     }
182 };
184 static double const DEGREE = M_PI/180.;
186 SvgAffineTest::test_t const SvgAffineTest::read_matrix_tests[3] = {
187     {"matrix(0,0,0,0,0,0)",Geom::Matrix(0,0,0,0,0,0)},
188     {" matrix(1,2,3,4,5,6)",Geom::Matrix(1,2,3,4,5,6)},
189     {"matrix (1 2 -3,-4,5e6,-6e-7)",Geom::Matrix(1,2,-3,-4,5e6,-6e-7)}};
190 SvgAffineTest::test_t const SvgAffineTest::read_translate_tests[3] = {
191     {"translate(1)",Geom::Matrix(1,0,0,1,1,0)},
192     {"translate(1,1)",Geom::Matrix(1,0,0,1,1,1)},
193     {"translate(-1e3 .123e2)",Geom::Matrix(1,0,0,1,-1e3,.123e2)}};
194 SvgAffineTest::test_t const SvgAffineTest::read_scale_tests[3] = {
195     {"scale(2)",Geom::Matrix(2,0,0,2,0,0)},
196     {"scale(2,3)",Geom::Matrix(2,0,0,3,0,0)},
197     {"scale(0.1e-2 -.475e0)",Geom::Matrix(0.1e-2,0,0,-.475e0,0,0)}};
198 SvgAffineTest::test_t const SvgAffineTest::read_rotate_tests[4] = {
199     {"rotate(13 )",Geom::Matrix(cos(13.*DEGREE),sin(13.*DEGREE),-sin(13.*DEGREE),cos(13.*DEGREE),0,0)},
200     {"rotate(-13)",Geom::Matrix(cos(-13.*DEGREE),sin(-13.*DEGREE),-sin(-13.*DEGREE),cos(-13.*DEGREE),0,0)},
201     {"rotate(373)",Geom::Matrix(cos(13.*DEGREE),sin(13.*DEGREE),-sin(13.*DEGREE),cos(13.*DEGREE),0,0)},
202     {"rotate(13,7,11)",Geom::Matrix(cos(13.*DEGREE),sin(13.*DEGREE),-sin(13.*DEGREE),cos(13.*DEGREE),(1-cos(13.*DEGREE))*7+sin(13.*DEGREE)*11,(1-cos(13.*DEGREE))*11-sin(13.*DEGREE)*7)}};
203 SvgAffineTest::test_t const SvgAffineTest::read_skew_tests[3] = {
204     {"skewX( 30)",Geom::Matrix(1,0,tan(30.*DEGREE),1,0,0)},
205     {"skewX(-30)",Geom::Matrix(1,0,tan(-30.*DEGREE),1,0,0)},
206     {"skewY(390)",Geom::Matrix(1,tan(30.*DEGREE),0,1,0,0)}};
207 char const * const SvgAffineTest::read_fail_tests[25] = {
208     "matrix((1,2,3,4,5,6)",
209     "matrix((1,2,3,4,5,6))",
210     "matrix(1,2,3,4,5,6))",
211     "matrix(,1,2,3,4,5,6)",
212     "matrix(1,2,3,4,5,6,)",
213     "matrix(1,2,3,4,5,)",
214     "matrix(1,2,3,4,5)",
215     "matrix(1,2,3,4,5e6-3)", // Here numbers HAVE to be separated by a comma-wsp sequence
216     "matrix(1,2,3,4,5e6.3)", // Here numbers HAVE to be separated by a comma-wsp sequence
217     "translate()",
218     "translate(,)",
219     "translate(1,)",
220     "translate(1,6,)",
221     "translate(1,6,0)",
222     "scale()",
223     "scale(1,6,2)",
224     "rotate()",
225     "rotate(1,6)",
226     "rotate(1,6,)",
227     "rotate(1,6,3,4)",
228     "skewX()",
229     "skewX(-)",
230     "skewX(.)",
231     "skewY(,)",
232     "skewY(1,2)"};
234 SvgAffineTest::test_t const SvgAffineTest::write_matrix_tests[2] = {
235     {"matrix(1,2,3,4,5,6)",Geom::Matrix(1,2,3,4,5,6)},
236     {"matrix(-1,2123,3,0.4,1e-8,1e20)",Geom::Matrix(-1,2.123e3,3+1e-14,0.4,1e-8,1e20)}};
237 SvgAffineTest::test_t const SvgAffineTest::write_translate_tests[3] = {
238     {"translate(1,1)",Geom::Matrix(1,0,0,1,1,1)},
239     {"translate(1)",Geom::Matrix(1,0,0,1,1,0)},
240     {"translate(-1345,0.123)",Geom::Matrix(1,0,0,1,-1.345e3,.123)}};
241 SvgAffineTest::test_t const SvgAffineTest::write_scale_tests[2] = {
242     {"scale(0)",Geom::Matrix(0,0,0,0,0,0)},
243     {"scale(2,3)",Geom::Matrix(2,0,0,3,0,0)}};
244 SvgAffineTest::test_t const SvgAffineTest::write_rotate_tests[2] = {
245     {"rotate(13)",Geom::Matrix(cos(13.*DEGREE),sin(13.*DEGREE),-sin(13.*DEGREE),cos(13.*DEGREE),0,0)},
246     {"rotate(-13,7,11)",Geom::Matrix(cos(-13.*DEGREE),sin(-13.*DEGREE),-sin(-13.*DEGREE),cos(-13.*DEGREE),(1-cos(-13.*DEGREE))*7+sin(-13.*DEGREE)*11,(1-cos(-13.*DEGREE))*11-sin(-13.*DEGREE)*7)}};
247 SvgAffineTest::test_t const SvgAffineTest::write_skew_tests[3] = {
248     {"skewX(30)",Geom::Matrix(1,0,tan(30.*DEGREE),1,0,0)},
249     {"skewX(-30)",Geom::Matrix(1,0,tan(-30.*DEGREE),1,0,0)},
250     {"skewY(390)",Geom::Matrix(1,tan(30.*DEGREE),0,1,0,0)}};
252 /*
253   Local Variables:
254   mode:c++
255   c-file-style:"stroustrup"
256   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
257   indent-tabs-mode:nil
258   fill-column:99
259   End:
260 */
261 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :