View Javadoc
1   package org.djutils.draw.point;
2   
3   import static org.junit.jupiter.api.Assertions.assertEquals;
4   import static org.junit.jupiter.api.Assertions.assertFalse;
5   import static org.junit.jupiter.api.Assertions.assertNotEquals;
6   import static org.junit.jupiter.api.Assertions.assertTrue;
7   import static org.junit.jupiter.api.Assertions.fail;
8   
9   import java.awt.geom.Point2D;
10  import java.util.Iterator;
11  import java.util.NoSuchElementException;
12  
13  import org.djutils.exceptions.Try;
14  import org.djutils.math.AngleUtil;
15  import org.junit.jupiter.api.Test;
16  
17  /**
18   * DirectedPoint2dTest.java.
19   * <p>
20   * Copyright (c) 2023-2025 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
21   * for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is
22   * distributed under a three-clause BSD-style license, which can be found at
23   * <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>.
24   * </p>
25   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
26   * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
27   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
28   */
29  public class DirectedPoint2dTest
30  {
31      /**
32       * Test the methods that are not covered by the Ray2dTest.
33       */
34      @SuppressWarnings({"unlikely-arg-type"})
35      @Test
36      public void testMethods()
37      {
38          DirectedPoint2d dp = new DirectedPoint2d(1, 2, 3);
39          assertEquals(1, dp.getX(), 0.0, "x can be retrieved");
40          assertEquals(2, dp.getY(), 0.0, "y can be retrieved");
41          assertEquals(3, dp.getDirZ(), 0.0, "DirZ can be retrieved");
42          assertEquals(1, dp.size(), "size is 1");
43          Iterator<Point2d> it = dp.iterator();
44          assertTrue(it.hasNext(), "iterator has at least one point to provide");
45          Point2d p = it.next();
46          assertEquals(p.x, dp.x, 0, "x matches");
47          assertEquals(p.y, dp.y, 0, "y matches");
48          assertFalse(it.hasNext(), "iterator is now exhausted");
49          DirectedPoint2d neg = dp.neg();
50          assertEquals(-1, neg.x, 0, "x is negated");
51          assertEquals(-2, neg.y, 0, "y is negated");
52          assertEquals(AngleUtil.normalizeAroundZero(3 + Math.PI), neg.dirZ, 0.0001, "dirZ is altered by pi");
53          try
54          {
55              it.next();
56              fail("exhausted iterator should have thrown an exception");
57          }
58          catch (NoSuchElementException nse)
59          {
60              // Ignore expected exception
61          }
62          assertTrue(dp.toString().startsWith("DirectedPoint2d"));
63          assertTrue(dp.toString(false).startsWith("DirectedPoint2d"));
64          assertTrue(dp.toString(true).startsWith("["));
65          assertEquals(dp, dp, "Equals to itself");
66          assertFalse(dp.equals("bla"), "Not equal to some random string");
67      }
68  
69      /**
70       * Test the DirectedPoint2d construction methods.
71       */
72      @SuppressWarnings("unlikely-arg-type")
73      @Test
74      public void testDirectedPoint2dConstruction()
75      {
76          DirectedPoint2d p = new DirectedPoint2d(10.0, -20.0, Math.PI);
77          assertEquals(10.0, p.x, 0, "x");
78          assertEquals(-20.0, p.y, 0, "y");
79          assertEquals(3.1415926, p.getDirZ(), 1E-6, "dirZ");
80  
81          Point2d p2d = new Point2d(10, -20);
82          p = new DirectedPoint2d(p2d, Math.PI);
83          assertEquals(10.0, p.x, 0, "x");
84          assertEquals(-20.0, p.y, 0, "y");
85          assertEquals(3.1415926, p.getDirZ(), 1E-6, "dirZ");
86          assertEquals(3.1415926, p.dirZ, 1E-6, "dirZ");
87  
88          try
89          {
90              new DirectedPoint2d(Double.NaN, 0, 0);
91              fail("NaN coordinate should have thrown an ArithmeticException");
92          }
93          catch (ArithmeticException e)
94          {
95              // Ignore expected exception
96          }
97  
98          try
99          {
100             new DirectedPoint2d(0, Double.NaN, 0);
101             fail("NaN coordinate should have thrown an ArithmeticException");
102         }
103         catch (ArithmeticException e)
104         {
105             // Ignore expected exception
106         }
107 
108         try
109         {
110             new DirectedPoint2d(0, 0, Double.NaN);
111             fail("NaN coordinate should have thrown an ArithmeticException");
112         }
113         catch (ArithmeticException e)
114         {
115             // Ignore expected exception
116         }
117 
118         double[] p2Arr = new double[] {5.0, 6.0};
119         p = new DirectedPoint2d(p2Arr, Math.PI / 2.0);
120         assertEquals(5.0, p.x, 1E-6);
121         assertEquals(6.0, p.y, 1E-6);
122         assertEquals(3.1415926 / 2.0, p.getDirZ(), 1E-6);
123         Point2D.Double p2DD = new Point2D.Double(-0.1, -0.2);
124         p = new DirectedPoint2d(p2DD, Math.PI / 4.0);
125         assertEquals(-0.1, p.x, 1E-6);
126         assertEquals(-0.2, p.y, 1E-6);
127         assertEquals(p2DD, p.toPoint2D());
128         assertEquals(3.1415926 / 4.0, p.getDirZ(), 1E-6);
129 
130         Try.testFail(new Try.Execution()
131         {
132             @Override
133             public void execute() throws Throwable
134             {
135                 new DirectedPoint2d((Point2D.Double) null, 0.0);
136             }
137         }, "Should throw NPE", NullPointerException.class);
138 
139         Try.testFail(new Try.Execution()
140         {
141             @Override
142             public void execute() throws Throwable
143             {
144                 new DirectedPoint2d((Point2D.Double) null, Math.PI);
145             }
146         }, "Should throw NPE", NullPointerException.class);
147 
148         Try.testFail(new Try.Execution()
149         {
150             @Override
151             public void execute() throws Throwable
152             {
153                 new DirectedPoint2d(new double[] {}, Math.PI / 2.0);
154             }
155         }, "Should throw IAE", IllegalArgumentException.class);
156 
157         Try.testFail(new Try.Execution()
158         {
159             @Override
160             public void execute() throws Throwable
161             {
162                 new DirectedPoint2d(new double[] {1.0}, Math.PI / 4.0);
163             }
164         }, "Should throw IAE", IllegalArgumentException.class);
165 
166         Try.testFail(new Try.Execution()
167         {
168             @Override
169             public void execute() throws Throwable
170             {
171                 new DirectedPoint2d(new double[] {1.0, 2.0, 3.0}, Math.PI);
172             }
173         }, "Should throw IAE", IllegalArgumentException.class);
174 
175         // equals and hashCode
176         assertTrue(p.equals(p));
177         assertEquals(p.hashCode(), p.hashCode());
178         DirectedPoint3d p3d = p.translate(1, 2, 3);
179         assertFalse(p.equals(p3d));
180         assertFalse(p.equals(null));
181         assertNotEquals(p3d.hashCode(), p.hashCode());
182         assertEquals(p.x + 1.0, p3d.x, 0.00001, "translated x");
183         assertEquals(p.y + 2.0, p3d.y, 0.00001, "translated y");
184         assertEquals(0 + 3.0, p3d.z, 0.00001, "translated z");
185         assertEquals(Math.PI / 4.0, p.getDirZ(), 1E-6);
186         assertTrue(p.equals(p.translate(0.0, 0.0)));
187         assertFalse(p.equals(p.translate(1.0, 0.0)));
188         assertFalse(p.equals(p.translate(0.0, 1.0)));
189         assertFalse(p.equals(p.rotate(0.1)));
190 
191         // toString
192         p = new DirectedPoint2d(10.0, 20.0, Math.PI);
193         assertEquals("DirectedPoint2d [x=10.000000, y=20.000000, dirZ=3.141593]", p.toString());
194         assertEquals("DirectedPoint2d [x=10.0, y=20.0, dirZ=3.1]", p.toString("%.1f"));
195         assertEquals("[x=10, y=20, dirZ=3]", p.toString("%.0f", true));
196 
197         // epsilonEquals
198         DirectedPoint3d p3 = new DirectedPoint3d(p.translate(0.001, 0.0, 0.0), 0, p.dirZ);
199         DirectedPoint3d ref = new DirectedPoint3d(p.translate(0, 0, 0), 0, p.dirZ);
200         assertTrue(ref.epsilonEquals(p3, 0.09, 0.001));
201         assertTrue(p3.epsilonEquals(ref, 0.09, 0.001));
202         assertFalse(ref.epsilonEquals(p3, 0.0009, 0.001));
203         assertFalse(p3.epsilonEquals(ref, 0.0009, 0.001));
204         p3 = p.translate(0.0, 0.001, 0.0);
205         assertTrue(ref.epsilonEquals(p3, 0.09, 0.001));
206         assertTrue(p3.epsilonEquals(ref, 0.09, 0.001));
207         assertFalse(ref.epsilonEquals(p3, 0.0009, 0.001));
208         assertFalse(p3.epsilonEquals(ref, 0.0009, 0.001));
209         DirectedPoint2d p2 = p.translate(0.001, 0.0);
210         assertTrue(p.epsilonEquals(p2, 0.09, 0.001), "all");
211         assertFalse(p.epsilonEquals(p2, 0.0009, 0.001), "dx");
212         p2 = p.translate(0.0, 0.001);
213         assertTrue(p.epsilonEquals(p2, 0.09, 0.001), "all");
214         assertFalse(p.epsilonEquals(p2, 0.0009, 0.001), "dy");
215         p3 = p.translate(0.0, 0.0, 0.001);
216         assertTrue(ref.epsilonEquals(p3, 0.09, 0.001));
217         assertTrue(p3.epsilonEquals(ref, 0.09, 0.001));
218         assertFalse(ref.epsilonEquals(p3, 0.0009, 0.001));
219         assertFalse(p3.epsilonEquals(ref, 0.0009, 0.001));
220         DirectedPoint2d dp2 = p.rotate(0.001);
221         assertTrue(p.epsilonEquals(dp2, 0.09, 0.009));
222         assertTrue(dp2.epsilonEquals(p, 0.09, 0.009));
223         assertFalse(p.epsilonEquals(dp2, 0.0009, 0.0009));
224         assertFalse(dp2.epsilonEquals(p, 0.0009, 0.0009));
225     }
226 
227     /**
228      * Test the DirectedPoint2d operators.
229      */
230     @Test
231     public void testDirectedPoint2dOperators()
232     {
233         DirectedPoint2d p = new DirectedPoint2d(-0.1, -0.2, -Math.PI / 7);
234         DirectedPoint2d out = new DirectedPoint2d(p.abs(), p.dirZ);
235         assertEquals(0.1, out.x, 1E-6, "x");
236         assertEquals(0.2, out.y, 1E-6, "y");
237         assertEquals(-Math.PI / 7, out.getDirZ(), 1E-6, "dirZ");
238 
239         Iterator<Point2d> i = p.iterator();
240         assertTrue(i.hasNext(), "iterator has one point");
241         assertEquals(p, i.next(), "iterator returns p");
242         assertFalse(i.hasNext(), "iterator does not have another point");
243 
244         out = p.neg();
245         assertEquals(0.1, out.x, 1E-6, "neg x");
246         assertEquals(0.2, out.y, 1E-6, "neg y");
247         assertEquals(Math.PI - Math.PI / 7, out.getDirZ(), 1E-6, "neg dirZ");
248 
249         out = p.scale(1.0);
250         assertEquals(-0.1, out.x, 1E-6, "x");
251         assertEquals(-0.2, out.y, 1E-6, "y");
252         assertEquals(-Math.PI / 7, out.getDirZ(), 1E-6, "dirZ");
253 
254         out = p.scale(10.0);
255         assertEquals(-1.0, out.x, 1E-6, "10 x");
256         assertEquals(-2.0, out.y, 1E-6, "10 y");
257         assertEquals(-Math.PI / 7, out.getDirZ(), 1E-6, "dirZ");
258 
259         out = p.translate(5.0, -1.0);
260         assertEquals(4.9, out.x, 1E-6, "x");
261         assertEquals(-1.2, out.y, 1E-6, "y");
262         assertEquals(-Math.PI / 7, out.getDirZ(), 1E-6, "dirZ");
263 
264         out = p.translate(1.0, 3.0);
265         assertEquals(0.9, out.x, 1E-6, "x");
266         assertEquals(2.8, out.y, 1E-6, "y");
267         assertEquals(-Math.PI / 7, out.getDirZ(), 1E-6, "dirZ");
268 
269         out = p.rotate(-Math.PI / 4);
270         assertEquals(-0.1, out.x, 1E-6, "x");
271         assertEquals(-0.2, out.y, 1E-6, "y");
272         assertEquals(-Math.PI / 7 - Math.PI / 4, out.getDirZ(), 1E-6, "dirZ");
273 
274         // interpolate
275         DirectedPoint2d p1 = new DirectedPoint2d(1.0, 1.0, 0.0);
276         DirectedPoint2d p2 = new DirectedPoint2d(5.0, 5.0, Math.PI / 2.0);
277         assertEquals(p1, p1.interpolate(p2, 0.0), "p1 interpolated to p2 at 0");
278         assertEquals(p2, p1.interpolate(p2, 1.0), "p1 interpolated to p2 at 1");
279         assertEquals(p2, p2.interpolate(p1, 0.0), "p2 interpolated to p1 at 0");
280         assertEquals(p1, p2.interpolate(p1, 1.0), "p2 interpolated to p1 at 1");
281         assertEquals(p1, p1.interpolate(p1, 0.0), "p1 interpolated to itself at 0");
282         assertEquals(new DirectedPoint2d(3.0, 3.0, Math.PI / 4.0), p1.interpolate(p2, 0.5), "interpolated at halfway");
283 
284         // distance
285         assertEquals(Math.sqrt(32.0), p1.distance(p2), 0.001);
286         assertEquals(32.0, p1.distanceSquared(p2), 0.001);
287         // FIXME
288         // assertEquals(Math.sqrt(32.0), p1.horizontalDistance(p2), 0.001);
289         // assertEquals(32.0, p1.horizontalDistanceSquared(p2), 0.001);
290 
291         // direction
292         // assertEquals(Math.toRadians(45.0), p2.horizontalDirection(), 0.001);
293         // assertEquals(Math.toRadians(45.0), p1.horizontalDirection(p2), 0.001);
294         // assertEquals(0.0, new DirectedPoint2d(0.0, 0.0, Math.PI / 4.0).horizontalDirection(), 0.001);
295 
296         // normalize
297         DirectedPoint2d pn = new DirectedPoint2d(p2.normalize(), p.dirZ);
298         assertEquals(1.0 / Math.sqrt(2.0), pn.x, 0.001);
299         assertEquals(1.0 / Math.sqrt(2.0), pn.y, 0.001);
300 
301         Try.testFail(new Try.Execution()
302         {
303             @Override
304             public void execute() throws Throwable
305             {
306                 new DirectedPoint2d(0.0, 0.0, Math.PI / 4.0).normalize();
307             }
308         }, "Should throw exception", IllegalArgumentException.class);
309 
310         Try.testFail(new Try.Execution()
311         {
312             @Override
313             public void execute() throws Throwable
314             {
315                 p1.translate(Double.NaN, 2);
316             }
317         }, "Should throw ArithmeticException", ArithmeticException.class);
318 
319         Try.testFail(new Try.Execution()
320         {
321             @Override
322             public void execute() throws Throwable
323             {
324                 p1.translate(1, Double.NaN);
325             }
326         }, "Should throw ArithmeticException", ArithmeticException.class);
327 
328         Try.testFail(new Try.Execution()
329         {
330             @Override
331             public void execute() throws Throwable
332             {
333                 p1.translate(Double.NaN, 2, 3);
334             }
335         }, "Should throw ArithmeticException", ArithmeticException.class);
336 
337         Try.testFail(new Try.Execution()
338         {
339             @Override
340             public void execute() throws Throwable
341             {
342                 p1.translate(1, Double.NaN, 3);
343             }
344         }, "Should throw ArithmeticException", ArithmeticException.class);
345 
346         Try.testFail(new Try.Execution()
347         {
348             @Override
349             public void execute() throws Throwable
350             {
351                 p1.translate(1, 2, Double.NaN);
352             }
353         }, "Should throw ArithmeticException", ArithmeticException.class);
354 
355     }
356 
357     /**
358      * Test the DirectedPoint2d operators for NPE.
359      */
360     @Test
361     public void testDirectedPoint2dOperatorsNPE()
362     {
363         final DirectedPoint2d p1 = new DirectedPoint2d(1.0, 1.0, Math.PI / 4.0);
364 
365         Try.testFail(new Try.Execution()
366         {
367             @Override
368             public void execute() throws Throwable
369             {
370                 p1.interpolate(null, 0.5);
371             }
372         }, "Should throw NPE", NullPointerException.class);
373 
374         Try.testFail(new Try.Execution()
375         {
376             @Override
377             public void execute() throws Throwable
378             {
379                 p1.distance(null);
380             }
381         }, "Should throw NPE", NullPointerException.class);
382 
383         Try.testFail(new Try.Execution()
384         {
385             @Override
386             public void execute() throws Throwable
387             {
388                 p1.distanceSquared(null);
389             }
390         }, "Should throw NPE", NullPointerException.class);
391 
392     }
393 
394 }