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 }