View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.geometry.euclidean.twod;
18  
19  import java.util.ArrayList;
20  import java.util.List;
21  
22  import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
23  import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext;
24  import org.junit.Assert;
25  
26  /** Helper class designed to assist with linecast test assertions in 2D.
27   */
28  public class LinecastChecker2D {
29  
30      private static final double TEST_EPS = 1e-10;
31  
32      private static final DoublePrecisionContext TEST_PRECISION =
33              new EpsilonDoublePrecisionContext(TEST_EPS);
34  
35      /** The linecastable target. */
36      private final Linecastable2D target;
37  
38      /** List of expected results from the line cast operation. */
39      private final List<ExpectedResult> expectedResults = new ArrayList<>();
40  
41      /** Construct a new instance that performs linecast assertions against the
42       * given target.
43       * @param target
44       */
45      LinecastChecker2D(final Linecastable2D target) {
46          this.target = target;
47      }
48  
49      /** Configure the instance to expect no results (an empty list from linecast() and null from
50       * linecastFirst()) from the next linecast operation performed by {@link #whenGiven(Line)}
51       * or {@link #whenGiven(LineConvexSubset)}.
52       * @return
53       */
54      public LinecastChecker2D expectNothing() {
55          expectedResults.clear();
56  
57          return this;
58      }
59  
60      /** Configure the instance to expect a linecast point with the given parameters on the next
61       * linecast operation. Multiple calls to this method and/or {@link #and(Vector2D, Vector2D)}
62       * create an internal ordered list of results.
63       * @param point
64       * @param normal
65       * @return
66       */
67      public LinecastChecker2D expect(final Vector2D point, final Vector2D normal) {
68          expectedResults.add(new ExpectedResult(point, normal));
69  
70          return this;
71      }
72  
73      /** Fluent API alias for {@link #expect(Vector2D, Vector2D)}.
74       * @param point
75       * @param normal
76       * @return
77       */
78      public LinecastChecker2D and(final Vector2D point, final Vector2D normal) {
79          return expect(point, normal);
80      }
81  
82      /** Perform {@link Linecastable2D#linecast(Line)} and {@link Linecastable2D#linecastFirst(Line)}
83       * operations using the given line and assert that the results match the configured expected
84       * values.
85       * @param line
86       */
87      public void whenGiven(final Line line) {
88          checkLinecastResults(target.linecast(line), line);
89          checkLinecastFirstResult(target.linecastFirst(line), line);
90      }
91  
92      /** Perform {@link Linecastable2D#linecast(LineConvexSubset)} and {@link Linecastable2D#linecastFirst(LineConvexSubset)}
93       * operations using the given line segment and assert that the results match the configured
94       * expected results.
95       * @param segment
96       */
97      public void whenGiven(final LineConvexSubset segment) {
98          final Line line = segment.getLine();
99  
100         checkLinecastResults(target.linecast(segment), line);
101         checkLinecastFirstResult(target.linecastFirst(segment), line);
102     }
103 
104     /** Check that the given set of linecast result points matches those expected.
105      * @param results
106      * @param line
107      */
108     private void checkLinecastResults(final List<LinecastPoint2D> results, final Line line) {
109         Assert.assertNotNull("Linecast result list cannot be null", results);
110         Assert.assertEquals("Unexpected result size for linecast", expectedResults.size(), results.size());
111 
112         for (int i = 0; i < expectedResults.size(); ++i) {
113             final LinecastPoint2D expected = toLinecastPoint(expectedResults.get(i), line);
114             final LinecastPoint2D actual = results.get(i);
115 
116             if (!eq(expected, actual)) {
117                 Assert.fail("Unexpected linecast point at index " + i + " expected " + expected +
118                         " but was " + actual);
119             }
120         }
121     }
122 
123     /** Check that the given linecastFirst result matches that expected.
124      * @param result
125      * @param line
126      */
127     private void checkLinecastFirstResult(final LinecastPoint2D result, final Line line) {
128         if (expectedResults.isEmpty()) {
129             Assert.assertNull("Expected linecastFirst result to be null", result);
130         } else {
131             final LinecastPoint2D expected = toLinecastPoint(expectedResults.get(0), line);
132 
133             Assert.assertNotNull("Expected linecastFirst result to not be null", result);
134 
135             if (!eq(expected, result)) {
136                 Assert.fail("Unexpected result from linecastFirst: expected " + expected +
137                         " but was " + result);
138             }
139         }
140     }
141 
142     /** Fluent API method for creating new instances.
143      * @param src
144      * @return
145      */
146     public static LinecastChecker2D with(final Linecastable2D src) {
147         return new LinecastChecker2D(src);
148     }
149 
150     /** Return true if the given linecast points are equivalent according to the test precision.
151      * @param a
152      * @param b
153      * @return
154      */
155     private static boolean eq(final LinecastPoint2D a, final LinecastPoint2D b) {
156         return a.getPoint().eq(b.getPoint(), TEST_PRECISION) &&
157                 a.getNormal().eq(b.getNormal(), TEST_PRECISION) &&
158                 a.getLine().equals(b.getLine()) &&
159                 TEST_PRECISION.eq(a.getAbscissa(), b.getAbscissa());
160     }
161 
162     /** Convert an {@link ExpectedResult} struct to a {@link LinecastPoint2D} instance
163      * using the given line.
164      * @param expected
165      * @param line
166      * @return
167      */
168     private static LinecastPoint2D toLinecastPoint(final ExpectedResult expected, final Line line) {
169         return new LinecastPoint2D(expected.getPoint(), expected.getNormal(), line);
170     }
171 
172     /** Class containing intermediate expected results for a linecast operation.
173      */
174     private static final class ExpectedResult {
175         private final Vector2D point;
176         private final Vector2D normal;
177 
178         ExpectedResult(final Vector2D point, final Vector2D normal) {
179             this.point = point;
180             this.normal = normal;
181         }
182 
183         public Vector2D getPoint() {
184             return point;
185         }
186 
187         public Vector2D getNormal() {
188             return normal;
189         }
190     }
191 }