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