1 #include <cxxtest/TestSuite.h>
3 #include <libnr/nr-matrix.h>
4 #include <libnr/nr-matrix-fns.h>
5 #include <libnr/nr-matrix-ops.h>
6 #include <libnr/nr-matrix-rotate-ops.h>
7 #include <libnr/nr-matrix-scale-ops.h>
8 #include <libnr/nr-point-matrix-ops.h>
9 #include <libnr/nr-rotate.h>
10 #include <libnr/nr-rotate-ops.h>
11 #include <libnr/nr-scale-ops.h>
12 #include <libnr/nr-scale-translate-ops.h>
13 #include <libnr/nr-translate.h>
14 #include <libnr/nr-translate-ops.h>
15 #include <libnr/nr-translate-scale-ops.h>
16 using NR::Matrix;
17 using NR::X;
18 using NR::Y;
20 inline bool point_equalp(NR::Point const &a, NR::Point const &b)
21 {
22 return ( NR_DF_TEST_CLOSE(a[X], b[X], 1e-5) &&
23 NR_DF_TEST_CLOSE(a[Y], b[Y], 1e-5) );
24 }
26 class NrMatrixTest : public CxxTest::TestSuite
27 {
28 public:
30 NrMatrixTest() :
31 m_id( NR::identity() ),
32 r_id( NR::Point(1, 0) ),
33 t_id( 0, 0 ),
34 c16( 1.0, 2.0,
35 3.0, 4.0,
36 5.0, 6.0),
37 r86( NR::Point(.8, .6) ),
38 mr86( r86 ),
39 t23( 2.0, 3.0 ),
40 s_id( 1.0, 1.0 )
41 {
42 }
43 virtual ~NrMatrixTest() {}
45 // createSuite and destroySuite get us per-suite setup and teardown
46 // without us having to worry about static initialization order, etc.
47 static NrMatrixTest *createSuite() { return new NrMatrixTest(); }
48 static void destroySuite( NrMatrixTest *suite ) { delete suite; }
50 Matrix const m_id;
51 NR::rotate const r_id;
52 NR::translate const t_id;
53 Matrix const c16;
54 NR::rotate const r86;
55 NR::Matrix const mr86;
56 NR::translate const t23;
57 NR::scale const s_id;
62 void testCtorsAssignmentOp(void)
63 {
64 Matrix const c16_copy(c16);
65 Matrix c16_eq(m_id);
66 c16_eq = c16;
67 for(unsigned i = 0; i < 6; ++i) {
68 TS_ASSERT_EQUALS( c16[i], 1.0 + i );
69 TS_ASSERT_EQUALS( c16[i], c16_copy[i] );
70 TS_ASSERT_EQUALS( c16[i], c16_eq[i] );
71 TS_ASSERT_EQUALS( m_id[i], double( i == 0 || i == 3 ) );
72 }
73 }
75 void testScaleCtor(void)
76 {
77 NR::scale const s(2.0, 3.0);
78 NR::Matrix const ms(s);
79 NR::Point const p(5.0, 7.0);
80 TS_ASSERT_EQUALS( p * s, NR::Point(10.0, 21.0) );
81 TS_ASSERT_EQUALS( p * ms, NR::Point(10.0, 21.0) );
82 }
84 void testRotateCtor(void)
85 {
86 NR::Point const p0(1.0, 0.0);
87 NR::Point const p90(0.0, 1.0);
88 TS_ASSERT_EQUALS( p0 * r86, NR::Point(.8, .6) );
89 TS_ASSERT_EQUALS( p0 * mr86, NR::Point(.8, .6) );
90 TS_ASSERT_EQUALS( p90 * r86, NR::Point(-.6, .8) );
91 TS_ASSERT_EQUALS( p90 * mr86, NR::Point(-.6, .8) );
92 TS_ASSERT( matrix_equalp(Matrix( r86 * r86 ),
93 mr86 * mr86,
94 1e-14) );
95 }
97 void testTranslateCtor(void)
98 {
99 NR::Matrix const mt23(t23);
100 NR::Point const b(-2.0, 3.0);
101 TS_ASSERT_EQUALS( b * t23, b * mt23 );
102 }
104 void testIdentity(void)
105 {
106 TS_ASSERT( m_id.test_identity() );
107 TS_ASSERT( Matrix(t_id).test_identity() );
108 TS_ASSERT( !(Matrix(NR::translate(-2, 3)).test_identity()) );
109 TS_ASSERT( Matrix(r_id).test_identity() );
110 NR::rotate const rot180(NR::Point(-1, 0));
111 TS_ASSERT( !(Matrix(rot180).test_identity()) );
112 TS_ASSERT( Matrix(s_id).test_identity() );
113 TS_ASSERT( !(Matrix(NR::scale(1.0, 0.0)).test_identity()) );
114 TS_ASSERT( !(Matrix(NR::scale(0.0, 1.0)).test_identity()) );
115 TS_ASSERT( !(Matrix(NR::scale(1.0, -1.0)).test_identity()) );
116 TS_ASSERT( !(Matrix(NR::scale(-1.0, -1.0)).test_identity()) );
117 }
119 void testInverse(void)
120 {
121 TS_ASSERT_EQUALS( m_id.inverse(), m_id );
122 TS_ASSERT_EQUALS( Matrix(t23).inverse(), Matrix(NR::translate(-2.0, -3.0)) );
123 NR::scale const s2(-4.0, 2.0);
124 NR::scale const sp5(-.25, .5);
125 TS_ASSERT_EQUALS( Matrix(s2).inverse(), Matrix(sp5) );
126 }
128 void testNrMatrixInvert(void)
129 {
130 NRMatrix const nr_m_id(m_id);
131 Matrix const m_s2(NR::scale(-4.0, 2.0));
132 NRMatrix const nr_s2(m_s2);
133 Matrix const m_sp5(NR::scale(-.25, .5));
134 NRMatrix const nr_sp5(m_sp5);
135 Matrix const m_t23(t23);
136 NRMatrix const nr_t23(m_t23);
137 NRMatrix inv;
138 nr_matrix_invert(&inv, &nr_m_id);
139 TS_ASSERT_EQUALS( Matrix(inv), m_id );
140 nr_matrix_invert(&inv, &nr_t23);
141 TS_ASSERT_EQUALS( Matrix(inv), Matrix(NR::translate(-2.0, -3.0)) );
142 nr_matrix_invert(&inv, &nr_s2);
143 TS_ASSERT_EQUALS( Matrix(inv), Matrix(nr_sp5) );
144 nr_matrix_invert(&inv, &nr_sp5);
145 TS_ASSERT_EQUALS( Matrix(inv), Matrix(nr_s2) );
147 /* Test that nr_matrix_invert handles src == dest. */
148 inv = nr_s2;
149 nr_matrix_invert(&inv, &inv);
150 TS_ASSERT_EQUALS( Matrix(inv), Matrix(nr_sp5) );
151 inv = nr_t23;
152 nr_matrix_invert(&inv, &inv);
153 TS_ASSERT_EQUALS( Matrix(inv), Matrix(NR::translate(-2.0, -3.0)) );
154 }
156 void testEllipticQuadraticForm(void)
157 {
158 NR::Matrix const aff(1.0, 1.0,
159 0.0, 1.0,
160 5.0, 6.0);
161 NR::Matrix const invaff = aff.inverse();
162 TS_ASSERT_EQUALS( invaff[1], -1.0 );
164 NR::Matrix const ef(elliptic_quadratic_form(invaff));
165 NR::Matrix const exp_ef(2, -1,
166 -1, 1,
167 0, 0);
168 TS_ASSERT_EQUALS( ef, exp_ef );
169 }
171 void testMatrixStarRotate(void)
172 {
173 NR::Matrix const ma(2.0, -1.0,
174 4.0, 4.0,
175 -0.5, 2.0);
176 NR::Matrix const a_r86( ma * r86 );
177 NR::Matrix const ma1( a_r86 * r86.inverse() );
178 TS_ASSERT( matrix_equalp(ma1, ma, 1e-12) );
179 NR::Matrix const exp_a_r86( 2*.8 + -1*-.6, 2*.6 + -1*.8,
180 4*.8 + 4*-.6, 4*.6 + 4*.8,
181 -.5*.8 + 2*-.6, -.5*.6 + 2*.8 );
182 TS_ASSERT( matrix_equalp(a_r86, exp_a_r86, 1e-12) );
183 }
185 void testTranslateStarScale_ScaleStarTranslate(void)
186 {
187 NR::translate const t2n4(2, -4);
188 NR::scale const sn2_8(-2, 8);
189 NR::Matrix const exp_ts(-2, 0,
190 0, 8,
191 -4, -32);
192 NR::Matrix const exp_st(-2, 0,
193 0, 8,
194 2, -4);
195 TS_ASSERT_EQUALS( exp_ts, t2n4 * sn2_8 );
196 TS_ASSERT_EQUALS( exp_st, sn2_8 * t2n4 );
197 }
199 void testMatrixStarScale(void)
200 {
201 NR::Matrix const ma(2.0, -1.0,
202 4.0, 4.0,
203 -0.5, 2.0);
204 NR::scale const sn2_8(-2, 8);
205 NR::Matrix const exp_as(-4, -8,
206 -8, 32,
207 1, 16);
208 TS_ASSERT_EQUALS( ma * sn2_8, exp_as );
209 }
210 };
212 /*
213 Local Variables:
214 mode:c++
215 c-file-style:"stroustrup"
216 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
217 indent-tabs-mode:nil
218 fill-column:99
219 End:
220 */
221 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :