PointCollectionTestBase.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.geometry.core.collection;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
import org.apache.commons.geometry.core.Point;
import org.apache.commons.numbers.core.Precision;
/** Base test class for point collection types.
* @param <P> Point type
*/
public abstract class PointCollectionTestBase<P extends Point<P>> {
public static final double EPS = 1e-10;
public static final Precision.DoubleEquivalence PRECISION =
Precision.doubleEquivalenceOfEpsilon(EPS);
/** Create an empty array of the target point type.
* @return empty array of the target point type
*/
protected abstract P[] createPointArray();
/** Get a list of points with {@code NaN} coordinates.
* @return list of points with {@code NaN} coordinates
*/
protected abstract List<P> getNaNPoints();
/** Get a list of points with infinite coordinates.
* @return list of points with infinite coordinates
*/
protected abstract List<P> getInfPoints();
/** Get {@code cnt} number of unique test points that differ from each other in
* each dimension by <em>at least</em> {@code eps}.
* @param cnt number of points to return
* @param eps minimum value that each point must differ from other points along
* each dimension
* @return list of test points
*/
protected abstract List<P> getTestPoints(int cnt, double eps);
/** Get a list of points that lie {@code dist} distance from {@code pt}.
* @param pt input point
* @param dist distance from {@code pt}
* @return list of points that lie {@code dist} distance from {@code pt}
*/
protected abstract List<P> getTestPointsAtDistance(P pt, double dist);
/** Get {@code cnt} number of unique test points that differ from each other in
* each dimension by <em>at least</em> {@code eps}. The returned list is shuffled
* using {@code rnd}.
* @param cnt number of points to return
* @param eps minimum value that each point must differ from other points along
* each dimension
* @param rnd random instance used to shuffle the order of the points
* @return randomly ordered list of test points
*/
protected List<P> getTestPoints(final int cnt, final double eps, final Random rnd) {
final List<P> pts = new ArrayList<>(getTestPoints(cnt, eps));
Collections.shuffle(pts, rnd);
return pts;
}
/** Return true if the given points are equivalent to each other using the given precision.
* @param a first point
* @param b second point
* @param precision precision context
* @return true if the two points are equivalent when compared using the given precision
*/
protected abstract boolean eq(P a, P b, Precision.DoubleEquivalence precision);
/** Compare two points with equal distances computed during a "closest first" ordering.
* @param a first point
* @param b second point
* @return comparison of the two points
*/
protected abstract int disambiguateNearToFarOrder(P a, P b);
/** Assert that {@code a} and {@code b} are equivalent using the given precision context.
* @param a first point
* @param b second point
* @param precision precision context
*/
protected void assertEq(final P a, final P b, final Precision.DoubleEquivalence precision) {
assertTrue(eq(a, b, precision), () -> "Expected " + a + " and " + b + " to be equivalent");
}
/** Assert that {@code a} and {@code b} are not equivalent using the given precision context.
* @param a first point
* @param b second point
* @param precision precision context
*/
protected void assertNotEq(final P a, final P b, final Precision.DoubleEquivalence precision) {
assertFalse(eq(a, b, precision), () -> "Expected " + a + " and " + b + " to not be equivalent");
}
/** Create a comparator for use in testing "near to far" ordering.
* @param refPt reference point
* @return comparator for use in testing "near to far" ordering
*/
protected Comparator<P> createNearToFarComparator(final P refPt) {
final Comparator<P> cmp = (a, b) -> Double.compare(a.distance(refPt), b.distance(refPt));
return cmp.thenComparing(this::disambiguateNearToFarOrder);
}
/** Create a comparator for use in testing "far to near" ordering.
* @param refPt reference point
* @return comparator for use in testing "far to near" ordering
*/
protected Comparator<P> createFarToNearComparator(final P refPt) {
return createNearToFarComparator(refPt).reversed();
}
/** Find the element in {@code list} farthest away from {@code refPt}.
* @param refPt reference point
* @param list list to search
* @return element in {@code list} farthest from {@code refPt}
*/
protected P findFarthest(final P refPt, final List<P> list) {
final Comparator<P> cmp = createFarToNearComparator(refPt);
P result = null;
for (final P pt : list) {
if (result == null || cmp.compare(pt, result) < 0) {
result = pt;
}
}
return result;
}
/** Return the maximum distance from {@code refPt} to the points in {@code pts}.
* @param <P> Point type
* @param refPt reference point
* @param pts test points
* @return maximum distance from {@code refPt} to the points in {@code pts}
*/
protected double findMaxDistance(final P refPt, final Collection<P> pts) {
double maxDist = 0d;
for (final P pt : pts) {
final double dist = pt.distance(refPt);
if (maxDist > dist) {
maxDist = dist;
}
}
return maxDist;
}
}