mirror of https://github.com/apache/lucene.git
Avoid generating invalid test polygons
This commit is contained in:
parent
ba81826951
commit
0dab327299
|
@ -188,6 +188,8 @@ Other
|
|||
|
||||
* GITHUB#11023: Removing some dead code in CheckIndex. (Jakub Slowinski)
|
||||
|
||||
* GITHUB#12757: Stop generating test polygons with duplicate vertices when the radius is small. (Stefan Vodita)
|
||||
|
||||
======================== Lucene 9.9.0 =======================
|
||||
|
||||
API Changes
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
*/
|
||||
package org.apache.lucene.document;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import org.apache.lucene.document.ShapeField.QueryRelation;
|
||||
import org.apache.lucene.geo.Component2D;
|
||||
import org.apache.lucene.geo.Tessellator;
|
||||
|
@ -286,6 +288,33 @@ public class TestXYShape extends LuceneTestCase {
|
|||
IOUtils.close(w, reader, dir);
|
||||
}
|
||||
|
||||
/** Checks if at least two of the given polygon's vertices are identical. */
|
||||
private boolean hasDuplicateVertices(XYPolygon poly) {
|
||||
Set<XYPoint> vertices = new HashSet<>();
|
||||
// The first and last vertices are always equal, so we don't check the last.
|
||||
for (int i = 0; i < poly.numPoints() - 1; i++) {
|
||||
XYPoint vertex = new XYPoint(poly.getPolyX(i), poly.getPolyY(i));
|
||||
if (vertices.contains(vertex)) {
|
||||
return true;
|
||||
}
|
||||
vertices.add(vertex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that for small radii and large numbers of vertices, we produce valid regular polygons.
|
||||
*/
|
||||
public void testRegularPolygonsAreLargeEnough() {
|
||||
final int maxGons = 100;
|
||||
// The more vertices, the larger radius we need for the polygon not to collapse into a point.
|
||||
double radius = random().nextDouble(0, ShapeTestUtil.smallestRadius(maxGons));
|
||||
int gons = random().nextInt(3, maxGons);
|
||||
|
||||
XYPolygon regularNGon = ShapeTestUtil.createRegularPolygon(0, 0, radius, gons);
|
||||
assertFalse(hasDuplicateVertices(regularNGon));
|
||||
}
|
||||
|
||||
private static boolean areBoxDisjoint(XYRectangle r1, XYRectangle r2) {
|
||||
return (r1.minX <= r2.minX && r1.minY <= r2.minY && r1.maxX >= r2.maxX && r1.maxY >= r2.maxY);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,9 @@ package org.apache.lucene.tests.geo;
|
|||
import com.carrotsearch.randomizedtesting.RandomizedContext;
|
||||
import com.carrotsearch.randomizedtesting.generators.BiasedNumbers;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import org.apache.lucene.geo.GeoUtils;
|
||||
import org.apache.lucene.geo.XYCircle;
|
||||
import org.apache.lucene.geo.XYEncodingUtils;
|
||||
|
@ -154,12 +156,14 @@ public class ShapeTestUtil {
|
|||
}
|
||||
|
||||
private static XYPolygon surpriseMePolygon(Random random) {
|
||||
|
||||
while (true) {
|
||||
float centerX = nextFloat(random);
|
||||
float centerY = nextFloat(random);
|
||||
double radius = 0.1 + 20 * random.nextDouble();
|
||||
double radiusDelta = random.nextDouble();
|
||||
|
||||
Set<XYPoint> vertices = new HashSet<>();
|
||||
ArrayList<Float> xList = new ArrayList<>();
|
||||
ArrayList<Float> yList = new ArrayList<>();
|
||||
double angle = 0.0;
|
||||
|
@ -183,6 +187,12 @@ public class ShapeTestUtil {
|
|||
float x = (float) (centerX + len * Math.cos(Math.toRadians(angle)));
|
||||
float y = (float) (centerY + len * Math.sin(Math.toRadians(angle)));
|
||||
|
||||
XYPoint vertex = new XYPoint(x, y);
|
||||
if (vertices.contains(vertex)) {
|
||||
// If we have already generated an identical vertex, ignore and try again.
|
||||
continue;
|
||||
}
|
||||
vertices.add(vertex);
|
||||
xList.add(x);
|
||||
yList.add(y);
|
||||
}
|
||||
|
@ -197,10 +207,44 @@ public class ShapeTestUtil {
|
|||
xArray[i] = xList.get(i);
|
||||
yArray[i] = yList.get(i);
|
||||
}
|
||||
if (xArray.length < 4 || yArray.length < 4) {
|
||||
// Not a valid polygon, try again.
|
||||
continue;
|
||||
}
|
||||
return new XYPolygon(xArray, yArray);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Polygons can get so small that their sides are shorter than a float is able to represent. For a
|
||||
* regular polygon, a smaller radius produces smaller sides, as does increasing the number of
|
||||
* vertices.
|
||||
*
|
||||
* <p>This method checks that we can represent the regular polygon with the given radius and
|
||||
* number of vertices.
|
||||
*/
|
||||
private static boolean regularPolygonFitsFloat(double radius, int gons) {
|
||||
// https://en.wikipedia.org/wiki/Regular_polygon
|
||||
double side = 2 * radius * Math.sin(Math.PI / gons);
|
||||
|
||||
// We need the difference between two vertices along at least one axis to be >= smallest
|
||||
// positive float.
|
||||
// To ensure that, we take the worst case, where the difference along the axes is equal.
|
||||
// Effectively, the side in this case is the hypothenuse of an isosceles right triangle
|
||||
// with catheti equal to the smallest positive float.
|
||||
double threshold = Float.MIN_VALUE * Math.sqrt(2);
|
||||
|
||||
return side > threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the radius of the smallest polygon with {@code gons} sides, whose vertices can be
|
||||
* represented as floats.
|
||||
*/
|
||||
public static double smallestRadius(int gons) {
|
||||
return (double) Float.MIN_VALUE / Math.sqrt(2) / Math.sin(Math.PI / gons);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes an n-gon, centered at the provided x/y, and each vertex approximately distanceMeters away
|
||||
* from the center.
|
||||
|
@ -210,6 +254,11 @@ public class ShapeTestUtil {
|
|||
public static XYPolygon createRegularPolygon(
|
||||
double centerX, double centerY, double radius, int gons) {
|
||||
|
||||
if (regularPolygonFitsFloat(radius, gons) == false) {
|
||||
// The provided radius is too small, use the smallest radius that will work.
|
||||
radius = smallestRadius(gons);
|
||||
}
|
||||
|
||||
double maxX =
|
||||
StrictMath.min(
|
||||
StrictMath.abs(Float.MAX_VALUE - centerX), StrictMath.abs(-Float.MAX_VALUE - centerX));
|
||||
|
|
Loading…
Reference in New Issue