LUCENE-9067: Polygon2D#contains is now thread safe (#1040)

Use a byte to handle the logic if a point lies on the boundary.
This commit is contained in:
Ignacio Vera 2019-11-27 12:09:23 +01:00 committed by GitHub
parent 8485b5a939
commit 14dc678f39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 34 additions and 19 deletions

View File

@ -145,6 +145,8 @@ Other
* LUCENE-8983: Add sandbox PhraseWildcardQuery to control multi-terms expansions in a phrase. (Bruno Roustant)
* LUCENE-9067: Polygon2D#contains() is now thread safe. (Ignacio Vera)
Build
* Upgrade forbiddenapis to version 2.7; upgrade Groovy to 2.4.17. (Uwe Schindler)

View File

@ -17,7 +17,6 @@
package org.apache.lucene.geo;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.apache.lucene.geo.GeoUtils.lineCrossesLine;
import static org.apache.lucene.geo.GeoUtils.lineCrossesLineWithBoundary;
@ -38,7 +37,7 @@ import static org.apache.lucene.geo.GeoUtils.orient;
*
* @lucene.internal
*/
public class EdgeTree {
public class EdgeTree {
// lat-lon pair (in original order) of the two vertices
final double y1, y2;
final double x1, x2;
@ -50,6 +49,10 @@ public class EdgeTree {
EdgeTree left;
/** right child edge, or null */
EdgeTree right;
/** helper bytes to signal if a point is on an edge, it is within the edge tree or disjoint */
final private static byte FALSE = 0x00;
final private static byte TRUE = 0x01;
final private static byte ON_EDGE = 0x02;
EdgeTree(double x1, double y1, double x2, double y2, double low, double max) {
this.y1 = y1;
@ -61,7 +64,17 @@ public class EdgeTree {
}
/**
* Returns true if the point crosses this edge subtree an odd number of times
* Returns true if the point is on an edge or crosses the edge subtree an odd number
* of times.
*/
protected boolean contains(double x, double y) {
return containsPnPoly(x, y) > FALSE;
}
/**
* Returns byte 0x00 if the point crosses this edge subtree an even number of times.
* Returns byte 0x01 if the point crosses this edge subtree an odd number of times.
* Returns byte 0x02 if the point is on one of the edges.
* <p>
* See <a href="https://www.ecse.rpi.edu/~wrf/Research/Short_Notes/pnpoly.html">
* https://www.ecse.rpi.edu/~wrf/Research/Short_Notes/pnpoly.html</a> for more information.
@ -90,30 +103,35 @@ public class EdgeTree {
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
protected boolean contains(double x, double y, AtomicBoolean isOnEdge) {
boolean res = false;
if (isOnEdge.get() == false && y <= this.max) {
private byte containsPnPoly(double x, double y) {
byte res = FALSE;
if (y <= this.max) {
if (y == this.y1 && y == this.y2 ||
(y <= this.y1 && y >= this.y2) != (y >= this.y1 && y <= this.y2)) {
if ((x == this.x1 && x == this.x2) ||
((x <= this.x1 && x >= this.x2) != (x >= this.x1 && x <= this.x2) &&
GeoUtils.orient(this.x1, this.y1, this.x2, this.y2, x, y) == 0)) {
// if its on the boundary return true
isOnEdge.set(true);
return true;
return ON_EDGE;
} else if (this.y1 > y != this.y2 > y) {
res = x < (this.x2 - this.x1) * (y - this.y1) / (this.y2 - this.y1) + this.x1;
res = x < (this.x2 - this.x1) * (y - this.y1) / (this.y2 - this.y1) + this.x1 ? TRUE : FALSE;
}
}
if (this.left != null) {
res ^= left.contains(x, y, isOnEdge);
res ^= left.containsPnPoly(x, y);
if ((res & 0x02) == 0x02) {
return ON_EDGE;
}
}
if (this.right != null && y >= this.low) {
res ^= right.contains(x, y, isOnEdge);
res ^= right.containsPnPoly(x, y);
if ((res & 0x02) == 0x02) {
return ON_EDGE;
}
}
}
return isOnEdge.get() || res;
assert res >= FALSE && res <= ON_EDGE;
return res;
}
/** returns true if the provided x, y point lies on the line */

View File

@ -16,8 +16,6 @@
*/
package org.apache.lucene.geo;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.lucene.index.PointValues.Relation;
/**
@ -41,8 +39,6 @@ public class Polygon2D implements Component2D {
final protected Component2D holes;
/** Edges of the polygon represented as a 2-d interval tree.*/
final EdgeTree tree;
/** helper boolean for points on boundary */
final private AtomicBoolean containsBoundary = new AtomicBoolean(false);
protected Polygon2D(final double minX, final double maxX, final double minY, final double maxY, double[] x, double[] y, Component2D holes) {
this.minY = minY;
@ -92,8 +88,7 @@ public class Polygon2D implements Component2D {
}
private boolean internalContains(double x, double y) {
containsBoundary.set(false);
if (tree.contains(x, y, containsBoundary)) {
if (tree.contains(x, y)) {
if (holes != null && holes.contains(x, y)) {
return false;
}