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.numbers.core.Precision;
    23  import org.junit.jupiter.api.Assertions;
    24
    25  /** Helper class designed to assist with linecast test assertions in 2D.
    26   */
    27  public class LinecastChecker2D {
    28
    29      private static final double TEST_EPS = 1e-10;
    30
    31      private static final Precision.DoubleEquivalence TEST_PRECISION =
    32              Precision.doubleEquivalenceOfEpsilon(TEST_EPS);
    33
    34      /** The linecastable target. */
    35      private final Linecastable2D target;
    36
    37      /** List of expected results from the line cast operation. */
    38      private final List<ExpectedResult> expectedResults = new ArrayList<>();
    39
    40      /** Construct a new instance that performs linecast assertions against the
    41       * given target.
    42       * @param target
    43       */
    44      LinecastChecker2D(final Linecastable2D target) {
    45          this.target = target;
    46      }
    47
    48      /** Configure the instance to expect no results (an empty list from linecast() and null from
    49       * linecastFirst()) from the next linecast operation performed by {@link #whenGiven(Line)}
    50       * or {@link #whenGiven(LineConvexSubset)}.
    51       * @return
    52       */
    53      public LinecastChecker2D expectNothing() {
    54          expectedResults.clear();
    55
    56          return this;
    57      }
    58
    59      /** Configure the instance to expect a linecast point with the given parameters on the next
    60       * linecast operation. Multiple calls to this method and/or {@link #and(Vector2D, Vector2D)}
    61       * create an internal ordered list of results.
    62       * @param point
    63       * @param normal
    64       * @return
    65       */
    66      public LinecastChecker2D expect(final Vector2D point, final Vector2D normal) {
    67          expectedResults.add(new ExpectedResult(point, normal));
    68
    69          return this;
    70      }
    71
    72      /** Fluent API alias for {@link #expect(Vector2D, Vector2D)}.
    73       * @param point
    74       * @param normal
    75       * @return
    76       */
    77      public LinecastChecker2D and(final Vector2D point, final Vector2D normal) {
    78          return expect(point, normal);
    79      }
    80
    81      /** Perform {@link Linecastable2D#linecast(Line)} and {@link Linecastable2D#linecastFirst(Line)}
    82       * operations using the given line and assert that the results match the configured expected
    83       * values.
    84       * @param line
    85       */
    86      public void whenGiven(final Line line) {
    87          checkLinecastResults(target.linecast(line), line);
    88          checkLinecastFirstResult(target.linecastFirst(line), line);
    89      }
    90
    91      /** Perform {@link Linecastable2D#linecast(LineConvexSubset)} and {@link Linecastable2D#linecastFirst(LineConvexSubset)}
    92       * operations using the given line segment and assert that the results match the configured
    93       * expected results.
    94       * @param segment
    95       */
    96      public void whenGiven(final LineConvexSubset segment) {
    97          final Line line = segment.getLine();
    98
    99          checkLinecastResults(target.linecast(segment), line);
    100         checkLinecastFirstResult(target.linecastFirst(segment), line);
    101     }
    102
    103     /** Check that the given set of linecast result points matches those expected.
    104      * @param results
    105      * @param line
    106      */
    107     private void checkLinecastResults(final List<? extends LinecastPoint2D> results, final Line line) {
    108         Assertions.assertNotNull(results, "Linecast result list cannot be null");
    109         Assertions.assertEquals(expectedResults.size(), results.size(), "Unexpected result size for linecast");
    110
    111         for (int i = 0; i < expectedResults.size(); ++i) {
    112             final LinecastPoint2D expected = toLinecastPoint(expectedResults.get(i), line);
    113             final LinecastPoint2D actual = results.get(i);
    114
    115             if (!eq(expected, actual)) {
    116                 Assertions.fail("Unexpected linecast point at index " + i + " expected " + expected +
    117                         " but was " + actual);
    118             }
    119         }
    120     }
    121
    122     /** Check that the given linecastFirst result matches that expected.
    123      * @param result
    124      * @param line
    125      */
    126     private void checkLinecastFirstResult(final LinecastPoint2D result, final Line line) {
    127         if (expectedResults.isEmpty()) {
    128             Assertions.assertNull(result, "Expected linecastFirst result to be null");
    129         } else {
    130             final LinecastPoint2D expected = toLinecastPoint(expectedResults.get(0), line);
    131
    132             Assertions.assertNotNull(result, "Expected linecastFirst result to not be null");
    133
    134             if (!eq(expected, result)) {
    135                 Assertions.fail("Unexpected result from linecastFirst: expected " + expected +
    136                         " but was " + result);
    137             }
    138         }
    139     }
    140
    141     /** Fluent API method for creating new instances.
    142      * @param src
    143      * @return
    144      */
    145     public static LinecastChecker2D with(final Linecastable2D src) {
    146         return new LinecastChecker2D(src);
    147     }
    148
    149     /** Return true if the given linecast points are equivalent according to the test precision.
    150      * @param a
    151      * @param b
    152      * @return
    153      */
    154     private static boolean eq(final LinecastPoint2D a, final LinecastPoint2D b) {
    155         return a.getPoint().eq(b.getPoint(), TEST_PRECISION) &&
    156                 a.getNormal().eq(b.getNormal(), TEST_PRECISION) &&
    157                 a.getLine().equals(b.getLine()) &&
    158                 TEST_PRECISION.eq(a.getAbscissa(), b.getAbscissa());
    159     }
    160
    161     /** Convert an {@link ExpectedResult} struct to a {@link LinecastPoint2D} instance
    162      * using the given line.
    163      * @param expected
    164      * @param line
    165      * @return
    166      */
    167     private static LinecastPoint2D toLinecastPoint(final ExpectedResult expected, final Line line) {
    168         return new LinecastPoint2D(expected.getPoint(), expected.getNormal(), line);
    169     }
    170
    171     /** Class containing intermediate expected results for a linecast operation.
    172      */
    173     private static final class ExpectedResult {
    174         private final Vector2D point;
    175         private final Vector2D normal;
    176
    177         ExpectedResult(final Vector2D point, final Vector2D normal) {
    178             this.point = point;
    179             this.normal = normal;
    180         }
    181
    182         public Vector2D getPoint() {
    183             return point;
    184         }
    185
    186         public Vector2D getNormal() {
    187             return normal;
    188         }
    189     }
    190 }