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