LUCENE-8071: Handle large concave circles properly. Committed on behalf of Ignacio Vera.

This commit is contained in:
Karl Wright 2017-12-01 10:44:58 -05:00
parent 832a975bc4
commit 6c3869f8b1
2 changed files with 21 additions and 47 deletions

View File

@ -35,13 +35,8 @@ class GeoExactCircle extends GeoBaseCircle {
protected final double cutoffAngle;
/** Actual accuracy */
protected final double actualAccuracy;
/** A point that is on the world and on the circle plane */
protected final GeoPoint[] edgePoints;
/** Notable points for a circle -- there aren't any */
protected static final GeoPoint[] circlePoints = new GeoPoint[0];
/** Slices of the circle. */
protected final List<CircleSlice> circleSlices;
@ -82,16 +77,17 @@ class GeoExactCircle extends GeoBaseCircle {
final GeoPoint eastPoint = planetModel.surfacePointOnBearing(center, cutoffAngle, Math.PI * 0.5);
final GeoPoint westPoint = planetModel.surfacePointOnBearing(center, cutoffAngle, Math.PI * 1.5);
final boolean mustSplit = cutoffAngle > Math.PI * 0.5;
final GeoPoint edgePoint;
if (planetModel.c > planetModel.ab) {
// z can be greater than x or y, so ellipse is longer in height than width
slices.add(new ApproximationSlice(center, eastPoint, Math.PI * 0.5, westPoint, Math.PI * -0.5, northPoint, 0.0));
slices.add(new ApproximationSlice(center, westPoint, Math.PI * 1.5, eastPoint, Math.PI * 0.5, southPoint, Math.PI));
slices.add(new ApproximationSlice(center, eastPoint, Math.PI * 0.5, westPoint, Math.PI * -0.5, northPoint, 0.0, mustSplit));
slices.add(new ApproximationSlice(center, westPoint, Math.PI * 1.5, eastPoint, Math.PI * 0.5, southPoint, Math.PI, mustSplit));
edgePoint = eastPoint;
} else {
// z will be less than x or y, so ellipse is shorter than it is tall
slices.add(new ApproximationSlice(center, northPoint, 0.0, southPoint, Math.PI, eastPoint, Math.PI * 0.5));
slices.add(new ApproximationSlice(center, southPoint, Math.PI, northPoint, Math.PI * 2.0, westPoint, Math.PI * 1.5));
slices.add(new ApproximationSlice(center, northPoint, 0.0, southPoint, Math.PI, eastPoint, Math.PI * 0.5, mustSplit));
slices.add(new ApproximationSlice(center, southPoint, Math.PI, northPoint, Math.PI * 2.0, westPoint, Math.PI * 1.5, mustSplit));
edgePoint = northPoint;
}
//System.out.println("Edgepoint = " + edgePoint);
@ -111,7 +107,7 @@ class GeoExactCircle extends GeoBaseCircle {
final GeoPoint interpPoint2 = planetModel.surfacePointOnBearing(center, cutoffAngle, interpPoint2Bearing);
// Is this point on the plane? (that is, is the approximation good enough?)
if (Math.abs(thisSlice.plane.evaluate(interpPoint1)) < actualAccuracy && Math.abs(thisSlice.plane.evaluate(interpPoint2)) < actualAccuracy) {
if (!thisSlice.mustSplit && Math.abs(thisSlice.plane.evaluate(interpPoint1)) < actualAccuracy && Math.abs(thisSlice.plane.evaluate(interpPoint2)) < actualAccuracy) {
circleSlices.add(new CircleSlice(thisSlice.plane, thisSlice.endPoint1, thisSlice.endPoint2, center, thisSlice.middlePoint));
//assert thisSlice.plane.isWithin(center);
} else {
@ -119,11 +115,11 @@ class GeoExactCircle extends GeoBaseCircle {
slices.add(new ApproximationSlice(center,
thisSlice.endPoint1, thisSlice.point1Bearing,
thisSlice.middlePoint, thisSlice.middlePointBearing,
interpPoint1, interpPoint1Bearing));
interpPoint1, interpPoint1Bearing, false));
slices.add(new ApproximationSlice(center,
thisSlice.middlePoint, thisSlice.middlePointBearing,
thisSlice.endPoint2, thisSlice.point2Bearing,
interpPoint2, interpPoint2Bearing));
interpPoint2, interpPoint2Bearing, false));
}
}
@ -177,12 +173,6 @@ class GeoExactCircle extends GeoBaseCircle {
@Override
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
if (circleSlices.size() == 0) {
return 0.0;
}
if (circleSlices.size() == 1) {
return distanceStyle.computeDistance(planetModel, circleSlices.get(0).circlePlane, x, y, z);
}
double outsideDistance = Double.POSITIVE_INFINITY;
for (final CircleSlice slice : circleSlices) {
final double distance = distanceStyle.computeDistance(planetModel, slice.circlePlane, x, y, z, slice.plane1, slice.plane2);
@ -195,9 +185,6 @@ class GeoExactCircle extends GeoBaseCircle {
@Override
public boolean isWithin(final double x, final double y, final double z) {
if (circleSlices.size() == 0) {
return true;
}
for (final CircleSlice slice : circleSlices) {
if (slice.circlePlane.isWithin(x, y, z) && slice.plane1.isWithin(x, y, z) && slice.plane2.isWithin(x, y, z)) {
return true;
@ -213,12 +200,6 @@ class GeoExactCircle extends GeoBaseCircle {
@Override
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) {
if (circleSlices.size() == 0) {
return false;
}
if (circleSlices.size() == 1) {
return circleSlices.get(0).circlePlane.intersects(planetModel, p, notablePoints, circlePoints, bounds);
}
for (final CircleSlice slice : circleSlices) {
if (slice.circlePlane.intersects(planetModel, p, notablePoints, slice.notableEdgePoints, bounds, slice.plane1, slice.plane2)) {
return true;
@ -229,13 +210,6 @@ class GeoExactCircle extends GeoBaseCircle {
@Override
public boolean intersects(GeoShape geoShape) {
if (circleSlices.size() == 0) {
return false;
}
if (circleSlices.size() == 1) {
return geoShape.intersects(circleSlices.get(0).circlePlane, circlePoints);
}
for (final CircleSlice slice : circleSlices) {
if (geoShape.intersects(slice.circlePlane, slice.notableEdgePoints, slice.plane1, slice.plane2)) {
return true;
@ -248,14 +222,7 @@ class GeoExactCircle extends GeoBaseCircle {
@Override
public void getBounds(Bounds bounds) {
super.getBounds(bounds);
if (circleSlices.size() == 0) {
return;
}
bounds.addPoint(center);
if (circleSlices.size() == 1) {
bounds.addPlane(planetModel, circleSlices.get(0).circlePlane);
return;
}
for (final CircleSlice slice : circleSlices) {
bounds.addPlane(planetModel, slice.circlePlane, slice.plane1, slice.plane2);
for (final GeoPoint point : slice.notableEdgePoints) {
@ -298,17 +265,19 @@ class GeoExactCircle extends GeoBaseCircle {
public final double point2Bearing;
public final GeoPoint middlePoint;
public final double middlePointBearing;
public final boolean mustSplit;
public ApproximationSlice(final GeoPoint center,
final GeoPoint endPoint1, final double point1Bearing,
final GeoPoint endPoint2, final double point2Bearing,
final GeoPoint middlePoint, final double middlePointBearing) {
final GeoPoint middlePoint, final double middlePointBearing, final boolean mustSplit) {
this.endPoint1 = endPoint1;
this.point1Bearing = point1Bearing;
this.endPoint2 = endPoint2;
this.point2Bearing = point2Bearing;
this.middlePoint = middlePoint;
this.middlePointBearing = middlePointBearing;
this.mustSplit = mustSplit;
// Construct the plane going through the three given points
this.plane = SidedPlane.constructNormalizedThreePointSidedPlane(center, endPoint1, endPoint2, middlePoint);
if (this.plane == null) {
@ -347,7 +316,4 @@ class GeoExactCircle extends GeoBaseCircle {
" plane 2 = " + plane2 + " notable edge points = " + notableEdgePoints + "}";
}
}
}

View File

@ -133,6 +133,14 @@ public class GeoExactCircleTest extends RandomGeo3dShapeGenerator{
assertTrue(exception);
}
public void testBigCircleInSphere() {
//In Planet model Sphere if circle is close to Math.PI we can get the situation where
//circle slice planes are bigger than half of a hemisphere. We need to make
//sure we divide the circle in at least 4 slices.
GeoCircle circle1 = GeoCircleFactory.makeExactGeoCircle(PlanetModel.SPHERE, 1.1306735252307394, -0.7374283438171261, 3.1415760537549234, 4.816939220262406E-12);
GeoPoint point = new GeoPoint(PlanetModel.SPHERE, -1.5707963267948966, 0.0);
assertTrue(circle1.isWithin(point));
}
/**
* in LUCENE-8054 we have problems with exact circles that have