1 package org.djutils.draw;
2
3 import static org.junit.Assert.assertEquals;
4 import static org.junit.Assert.assertTrue;
5 import static org.junit.Assert.fail;
6
7 import java.util.Arrays;
8
9 import org.djutils.draw.bounds.Bounds2d;
10 import org.djutils.draw.point.Point2d;
11 import org.junit.Test;
12
13
14
15
16
17
18
19
20
21
22 public class Transform2dTest
23 {
24
25
26
27 @Test
28 public void testMatrixMultiplication()
29 {
30 double[] mA = new double[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
31 double[] mB = new double[] { 2, 1, 0, 2, 4, 3, 3, 1, 2 };
32 double[] mAmB = Transform2d.mulMatMat(mA, mB);
33 double[] expected = new double[] { 15, 12, 12, 36, 30, 27, 57, 48, 42 };
34 for (int i = 0; i < 9; i++)
35 {
36 if (mAmB[i] != expected[i])
37 {
38 fail(String.format("difference MA x MB at %d: expected %f, was: %f", i, expected[i], mAmB[i]));
39 }
40 }
41
42 double[] m = new double[] { 1, 4, 2, 5, 3, 1, 4, 2, 5 };
43 double[] v = new double[] { 2, 5, 1 };
44 double[] mv = Transform2d.mulMatVec(m, v);
45 double[] ev = new double[] { 24, 26, 23 };
46 for (int i = 0; i < 3; i++)
47 {
48 if (mv[i] != ev[i])
49 {
50 fail(String.format("difference M x V at %d: expected %f, was: %f", i, ev[i], mv[i]));
51 }
52 }
53
54 v = new double[] { 1, 2 };
55 mv = Transform2d.mulMatVec2(m, v);
56 ev = new double[] { 11, 12 };
57 for (int i = 0; i < 2; i++)
58 {
59 if (mv[i] != ev[i])
60 {
61 fail(String.format("difference M x V3 at %d: expected %f, was: %f", i, ev[i], mv[i]));
62 }
63 }
64 }
65
66
67
68
69 @Test
70 public void testConstructor()
71 {
72
73 Transform2d t = new Transform2d();
74 assertEquals("matrix contians 9 values", 9, t.getMat().length);
75 for (int row = 0; row < 3; row++)
76 {
77 for (int col = 0; col < 3; col++)
78 {
79 int e = row == col ? 1 : 0;
80 assertEquals("Value in identity matrix matches", e, t.getMat()[3 * row + col], 0);
81 }
82 }
83 }
84
85
86
87
88 @Test
89 public void testTranslateScaleRotateShearAndReflect()
90 {
91 Transform2d t;
92
93 double[] values = new double[] { -100000, -100, -10, -3, -1, -0.3, -0.1, 0, 0.1, 0.3, 1, 3, 10, 100, 100000 };
94 for (double dx : values)
95 {
96 for (double dy : values)
97 {
98
99 t = new Transform2d();
100 t.translate(dx, dy);
101 for (double px : values)
102 {
103 for (double py : values)
104 {
105 Point2d p = t.transform(new Point2d(px, py));
106 assertEquals("translated x matches", px + dx, p.x, 0.001);
107 assertEquals("translated y matches", py + dy, p.y, 0.001);
108 double[] result = t.transform(new double[] { px, py });
109 assertEquals("translated x matches", px + dx, result[0], 0.001);
110 assertEquals("translated y matches", py + dy, result[1], 0.001);
111 }
112 }
113
114 t = new Transform2d();
115 t.translate(new Point2d(dx, dy));
116 for (double px : values)
117 {
118 for (double py : values)
119 {
120 Point2d p = t.transform(new Point2d(px, py));
121 assertEquals("transformed x matches", px + dx, p.x, 0.001);
122 assertEquals("transformed y matches", py + dy, p.y, 0.001);
123 double[] result = t.transform(new double[] { px, py });
124 assertEquals("transformed x matches", px + dx, result[0], 0.001);
125 assertEquals("transformed y matches", py + dy, result[1], 0.001);
126 }
127 }
128
129 t = new Transform2d();
130 t.scale(dx, dy);
131 for (double px : values)
132 {
133 for (double py : values)
134 {
135 Point2d p = t.transform(new Point2d(px, py));
136 assertEquals("scaled x matches", px * dx, p.x, 0.001);
137 assertEquals("scaled y matches", py * dy, p.y, 0.001);
138 double[] result = t.transform(new double[] { px, py });
139 assertEquals("scaled x matches", px * dx, result[0], 0.001);
140 assertEquals("scaled y matches", py * dy, result[1], 0.001);
141 }
142 }
143
144 t = new Transform2d();
145 t.shear(dx, dy);
146 for (double px : values)
147 {
148 for (double py : values)
149 {
150 Point2d p = t.transform(new Point2d(px, py));
151 assertEquals("sheared x matches", px + py * dx, p.x, 0.001);
152 assertEquals("sheared y matches", py + px * dy, p.y, 0.001);
153 double[] result = t.transform(new double[] { px, py });
154 assertEquals("sheared x matches", px + py * dx, result[0], 0.001);
155 assertEquals("sheared y matches", py + px * dy, result[1], 0.001);
156 }
157 }
158 }
159
160 t = new Transform2d();
161 t.rotation(dx);
162 double sine = Math.sin(dx);
163 double cosine = Math.cos(dx);
164 for (double px : values)
165 {
166 for (double py : values)
167 {
168 Point2d p = t.transform(new Point2d(px, py));
169 assertEquals("rotated x matches", px * cosine - py * sine, p.x, 0.001);
170 assertEquals("rotated y matches", py * cosine + px * sine, p.y, 0.001);
171 double[] result = t.transform(new double[] { px, py });
172 assertEquals("rotated x matches", px * cosine - py * sine, result[0], 0.001);
173 assertEquals("rotated y matches", py * cosine + px * sine, result[1], 0.001);
174 }
175 }
176 }
177
178 t = new Transform2d();
179 t.reflectX();
180 for (double px : values)
181 {
182 for (double py : values)
183 {
184 Point2d p = t.transform(new Point2d(px, py));
185 assertEquals("x-reflected x matches", -px, p.x, 0.001);
186 assertEquals("x-reflected y matches", py, p.y, 0.001);
187 double[] result = t.transform(new double[] { px, py });
188 assertEquals("x-reflected x matches", -px, result[0], 0.001);
189 assertEquals("x-reflected y matches", py, result[1], 0.001);
190 }
191 }
192
193 t = new Transform2d();
194 t.reflectY();
195 for (double px : values)
196 {
197 for (double py : values)
198 {
199 Point2d p = t.transform(new Point2d(px, py));
200 assertEquals("y-reflected x matches", px, p.x, 0.001);
201 assertEquals("y-reflected y matches", -py, p.y, 0.001);
202 double[] result = t.transform(new double[] { px, py });
203 assertEquals("y-reflected x matches", px, result[0], 0.001);
204 assertEquals("y-reflected y matches", -py, result[1], 0.001);
205 }
206 }
207 }
208
209
210
211
212 @Test
213 public void transformTest()
214 {
215 Transform2d reflectionX = new Transform2d().reflectX();
216 Transform2d reflectionY = new Transform2d().reflectY();
217
218 double[] values = new double[] { -100, -0.1, 0, 0.01, 1, 100 };
219 for (double translateX : values)
220 {
221 for (double translateY : values)
222 {
223 Transform2d translation = new Transform2d().translate(translateX, translateY);
224 for (double scaleX : values)
225 {
226 for (double scaleY : values)
227 {
228 Transform2d scaling = new Transform2d().scale(scaleX, scaleY);
229 for (double angle : new double[] { -2, 0, 0.5 })
230 {
231 Transform2d rotation = new Transform2d().rotation(angle);
232 for (double shearX : values)
233 {
234 for (double shearY : values)
235 {
236 Transform2d t = new Transform2d().translate(translateX, translateY).scale(scaleX, scaleY)
237 .rotation(angle).shear(shearX, shearY);
238 Transform2d tReflectX = new Transform2d().reflectX().translate(translateX, translateY)
239 .scale(scaleX, scaleY).rotation(angle).shear(shearX, shearY);
240 Transform2d tReflectY = new Transform2d().reflectY().translate(translateX, translateY)
241 .scale(scaleX, scaleY).rotation(angle).shear(shearX, shearY);
242 Transform2d shearing = new Transform2d().shear(shearX, shearY);
243 for (double px : values)
244 {
245 for (double py : values)
246 {
247 Point2d p = new Point2d(px, py);
248 Point2d tp = t.transform(p);
249 Point2d chainP = translation
250 .transform(scaling.transform(rotation.transform(shearing.transform(p))));
251 assertEquals("X", chainP.x, tp.x, 0.0000001);
252 assertEquals("Y", chainP.y, tp.y, 0.0000001);
253 tp = tReflectX.transform(p);
254 Point2d chainPReflectX = reflectionX.transform(chainP);
255 assertEquals("RX X", chainPReflectX.x, tp.x, 0.0000001);
256 assertEquals("RX Y", chainPReflectX.y, tp.y, 0.0000001);
257 tp = tReflectY.transform(p);
258 Point2d chainPReflectY = reflectionY.transform(chainP);
259 assertEquals("RY X", chainPReflectY.x, tp.x, 0.0000001);
260 assertEquals("RY Y", chainPReflectY.y, tp.y, 0.0000001);
261 }
262 }
263 }
264 }
265 }
266 }
267 }
268 }
269 }
270 }
271
272
273
274
275 @Test
276 public void transformBounds2dTest()
277 {
278 double[] values = new double[] { -100, 0.1, 0, 0.1, 100 };
279 double[] sizes = new double[] { 0, 10, 100 };
280 Transform2d t = new Transform2d().rotation(0.4).reflectX().scale(0.5, 1.5).shear(2, 3).translate(123, 456);
281
282 for (double x : values)
283 {
284 for (double y : values)
285 {
286 for (double xSize : sizes)
287 {
288 for (double ySize : sizes)
289 {
290 Bounds2d bb = new Bounds2d(x, x + xSize, y, y + ySize);
291 Point2d[] points = new Point2d[] { new Point2d(x, y), new Point2d(x + xSize, y),
292 new Point2d(x, y + ySize), new Point2d(x + xSize, y + ySize) };
293 Point2d[] transformedPoints = new Point2d[4];
294 for (int i = 0; i < points.length; i++)
295 {
296 transformedPoints[i] = t.transform(points[i]);
297 }
298 Bounds2d expected = new Bounds2d(Arrays.stream(transformedPoints).iterator());
299 Bounds2d got = t.transform(bb);
300 assertEquals("bb minX", expected.getMinX(), got.getMinX(), 0.0001);
301 assertEquals("bb maxX", expected.getMaxX(), got.getMaxX(), 0.0001);
302 assertEquals("bb minY", expected.getMinY(), got.getMinY(), 0.0001);
303 assertEquals("bb maxY", expected.getMaxY(), got.getMaxY(), 0.0001);
304 }
305 }
306 }
307 }
308 }
309
310
311
312
313 @Test
314 public void testBoundingRectangle2d()
315 {
316 Bounds2d bounds = new Bounds2d(-4, 4, -4, 4);
317
318
319 Transform2d transform = new Transform2d();
320 Bounds2d b = transform.transform(bounds);
321 testBounds2d(b, -4, 4, -4, 4);
322
323
324 transform = new Transform2d();
325 transform.translate(20, 10);
326 b = transform.transform(bounds);
327 testBounds2d(b, 20 - 4, 20 + 4, 10 - 4, 10 + 4);
328
329
330 transform = new Transform2d();
331 transform.rotation(Math.toRadians(90.0));
332 b = transform.transform(bounds);
333 testBounds2d(b, -4, 4, -4, 4);
334
335
336 transform = new Transform2d();
337 transform.rotation(Math.toRadians(45.0));
338 double d = 4.0 * Math.sqrt(2.0);
339 b = transform.transform(bounds);
340 testBounds2d(b, -d, d, -d, d);
341
342
343
344
345 transform = new Transform2d();
346 transform.translate(10, 20);
347 transform.rotation(Math.toRadians(45.0));
348 b = transform.transform(bounds);
349 testBounds2d(b, 10 - d, 10 + d, 20 - d, 20 + d);
350 }
351
352
353
354
355
356
357
358
359
360 private void testBounds2d(final Bounds2d b, final double minX, final double maxX, final double minY, final double maxY)
361 {
362 assertEquals(minX, b.getMinX(), 0.001);
363 assertEquals(maxX, b.getMaxX(), 0.001);
364 assertEquals(minY, b.getMinY(), 0.001);
365 assertEquals(maxY, b.getMaxY(), 0.001);
366 }
367
368
369
370
371 @Test
372 public void toStringTest()
373 {
374 assertTrue("toString returns something descriptive", new Transform2d().toString().startsWith("Transform2d "));
375 }
376
377 }