You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by kw...@apache.org on 2016/04/29 02:02:33 UTC

[01/26] lucene-solr:master: First cut of new class

Repository: lucene-solr
Updated Branches:
  refs/heads/master 791d1e739 -> 7bc50ec1e


First cut of new class


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/a5e24b21
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/a5e24b21
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/a5e24b21

Branch: refs/heads/master
Commit: a5e24b213d01030f95f4f36dd2c08d5ddbad1467
Parents: 38ebd90
Author: Karl Wright <Da...@gmail.com>
Authored: Sat Apr 23 03:48:09 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Sat Apr 23 03:48:09 2016 -0400

----------------------------------------------------------------------
 .../spatial3d/geom/GeoComplexPolygon.java       | 112 +++++++++++++++++++
 1 file changed, 112 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/a5e24b21/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
new file mode 100644
index 0000000..7fc53e8
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
@@ -0,0 +1,112 @@
+/*
+ * 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
+ *
+ *     http://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.lucene.spatial3d.geom;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * GeoComplexPolygon objects are structures designed to handle very large numbers of edges.
+ * They perform very well in this case compared to the alternatives, which all have O(N) evaluation
+ * and O(N^2) setup times.  Complex polygons have O(N) setup times and best case O(log(N))
+ * evaluation times.
+ *
+ * The tradeoff is that these objects perform object creation when evaluating intersects() and
+ * isWithin().
+ *
+ * @lucene.internal
+ */
+class GeoComplexPolygon extends GeoBasePolygon {
+  
+  /**
+   * Create a complex polygon from multiple lists of points, and a single point which is known to be in or out of
+   * set.
+   *@param planetModel is the planet model.
+   *@param pointsList is the list of lists of edge points.  The edge points describe edges, and have an implied
+   *  return boundary, so that N edges require N points.  These points have furthermore been filtered so that
+   *  no adjacent points are identical (within the bounds of the definition used by this package).  It is assumed
+   *  that no edges intersect, but the structure can contain both outer rings as well as holes.
+   *@param testPoint is the point whose in/out of setness is known.
+   *@param testPointInSet is true if the test point is considered "within" the polygon.
+   */
+  public GeoComplexPolygon(final PlanetModel planetModel, final List<List<GeoPoint>> pointsList, final GeoPoint testPoint, final boolean testPointInSet) {
+    super(planetModel);
+    // MHL
+  }
+
+  /** Compute a legal point index from a possibly illegal one, that may have wrapped.
+   *@param index is the index.
+   *@return the normalized index.
+   */
+  protected int legalIndex(int index) {
+    while (index >= points.size())
+      index -= points.size();
+    while (index < 0) {
+      index += points.size();
+    }
+    return index;
+  }
+
+  @Override
+  public boolean isWithin(final double x, final double y, final double z) {
+    // MHL
+    return false;
+  }
+  
+  @Override
+  public GeoPoint[] getEdgePoints() {
+    return edgePoints;
+  }
+
+  @Override
+  public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) {
+    // MHL
+    return false;
+  }
+
+
+  @Override
+  public void getBounds(Bounds bounds) {
+    super.getBounds(bounds);
+    // MHL
+  }
+
+  @Override
+  protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+    // MHL
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    // MHL
+    return false;
+  }
+
+  @Override
+  public int hashCode() {
+    // MHL
+  }
+
+  @Override
+  public String toString() {
+    return "GeoComplexPolygon: {planetmodel=" + planetModel + "}";
+  }
+}
+  


[08/26] lucene-solr:master: Get it to compile

Posted by kw...@apache.org.
Get it to compile


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/641d221c
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/641d221c
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/641d221c

Branch: refs/heads/master
Commit: 641d221cba408d63361f1d7ff428008bbaa4c7ea
Parents: 8b50cb8
Author: Karl Wright <Da...@gmail.com>
Authored: Mon Apr 25 14:33:38 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Mon Apr 25 14:33:38 2016 -0400

----------------------------------------------------------------------
 .../spatial3d/geom/GeoComplexPolygon.java       | 55 ++++++++------------
 .../org/apache/lucene/spatial3d/geom/Plane.java | 12 +----
 2 files changed, 25 insertions(+), 42 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/641d221c/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
index 229f9f4..9b6f670 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
@@ -35,11 +35,12 @@ import java.util.Map;
  */
 class GeoComplexPolygon extends GeoBasePolygon {
   
-  private final XTree xtree = new XTree();
-  private final YTree ytree = new YTree();
-  private final ZTree ztree = new ZTree();
+  private final XTree xTree = new XTree();
+  private final YTree yTree = new YTree();
+  private final ZTree zTree = new ZTree();
   
   private final boolean testPointInSet;
+  private final GeoPoint testPoint;
   
   private final Plane testPointXZPlane;
   private final Plane testPointYZPlane;
@@ -62,6 +63,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
   public GeoComplexPolygon(final PlanetModel planetModel, final List<List<GeoPoint>> pointsList, final GeoPoint testPoint, final boolean testPointInSet) {
     super(planetModel);
     this.testPointInSet = testPointInSet;
+    this.testPoint = testPoint;
     
     this.testPointXZPlane = new Plane(0.0, 1.0, 0.0, -testPoint.y);
     this.testPointYZPlane = new Plane(1.0, 0.0, 0.0, -testPoint.x);
@@ -71,15 +73,15 @@ class GeoComplexPolygon extends GeoBasePolygon {
     this.shapeStartEdges = new Edge[pointsList.size()];
     int edgePointIndex = 0;
     for (final List<GeoPoint> shapePoints : pointsList) {
-      GeoPoint lastGeoPoint = pointsList.get(shapePoints.size()-1);
+      GeoPoint lastGeoPoint = shapePoints.get(shapePoints.size()-1);
       edgePoints[edgePointIndex] = lastGeoPoint;
       Edge lastEdge = null;
       Edge firstEdge = null;
       for (final GeoPoint thisGeoPoint : shapePoints) {
         final Edge edge = new Edge(planetModel, lastGeoPoint, thisGeoPoint);
-        xtree.add(edge);
-        ytree.add(edge);
-        ztree.add(edge);
+        xTree.add(edge);
+        yTree.add(edge);
+        zTree.add(edge);
         // Now, link
         if (firstEdge == null) {
           firstEdge = edge;
@@ -98,19 +100,6 @@ class GeoComplexPolygon extends GeoBasePolygon {
     }
   }
 
-  /** Compute a legal point index from a possibly illegal one, that may have wrapped.
-   *@param index is the index.
-   *@return the normalized index.
-   */
-  protected int legalIndex(int index) {
-    while (index >= points.size())
-      index -= points.size();
-    while (index < 0) {
-      index += points.size();
-    }
-    return index;
-  }
-
   @Override
   public boolean isWithin(final double x, final double y, final double z) {
     return isWithin(new Vector(x, y, z));
@@ -181,7 +170,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
         final SidedPlane checkPointOtherCutoffPlane = new SidedPlane(thePoint, travelPlane, intersectionPoints[0]);
         // Note: we need to handle the cases where end point of the leg sits on an edge!
         // MHL
-        final CrossingEdgeIterator testPointEdgeIterator = new CrossingEdgeIterator(testPointYZPlane, testPointCutoffPlane, testPointOtherCutoffPlane);
+        final CrossingEdgeIterator testPointEdgeIterator = new CrossingEdgeIterator(testPointYZPlane, testPointCutoffPlane, testPointOtherCutoffPlane, null);
         xTree.traverse(testPointEdgeIterator, testPoint.x, testPoint.x);
         final CrossingEdgeIterator checkPointEdgeIterator = new CrossingEdgeIterator(travelPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane, thePoint);
         if (!yTree.traverse(checkPointEdgeIterator, thePoint.y, thePoint.y)) {
@@ -204,7 +193,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
         final SidedPlane checkPointOtherCutoffPlane = new SidedPlane(thePoint, travelPlane, intersectionPoints[0]);
         // Note: we need to handle the cases where end point of the leg sits on an edge!
         // MHL
-        final CrossingEdgeIterator testPointEdgeIterator = new CrossingEdgeIterator(testPointXYPlane, testPointCutoffPlane, testPointOtherCutoffPlane);
+        final CrossingEdgeIterator testPointEdgeIterator = new CrossingEdgeIterator(testPointXYPlane, testPointCutoffPlane, testPointOtherCutoffPlane, null);
         zTree.traverse(testPointEdgeIterator, testPoint.z, testPoint.z);
         final CrossingEdgeIterator checkPointEdgeIterator = new CrossingEdgeIterator(travelPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane, thePoint);
         if (!xTree.traverse(checkPointEdgeIterator, thePoint.x, thePoint.x)) {
@@ -227,7 +216,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
         final SidedPlane checkPointOtherCutoffPlane = new SidedPlane(thePoint, travelPlane, intersectionPoints[0]);
         // Note: we need to handle the cases where end point of the first leg sits on an edge!
         // MHL
-        final CrossingEdgeIterator testPointEdgeIterator = new CrossingEdgeIterator(testPointXZPlane, testPointCutoffPlane, testPointOtherCutoffPlane);
+        final CrossingEdgeIterator testPointEdgeIterator = new CrossingEdgeIterator(testPointXZPlane, testPointCutoffPlane, testPointOtherCutoffPlane, null);
         yTree.traverse(testPointEdgeIterator, testPoint.y, testPoint.y);
         final CrossingEdgeIterator checkPointEdgeIterator = new CrossingEdgeIterator(travelPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane, thePoint);
         if (!zTree.traverse(checkPointEdgeIterator, thePoint.z, thePoint.z)) {
@@ -251,7 +240,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
     final EdgeIterator intersector = new IntersectorEdgeIterator(p, notablePoints, bounds);
     // First, compute the bounds for the the plane
     final XYZBounds xyzBounds = new XYZBounds();
-    p.recordBounds(xyzBounds);
+    p.recordBounds(planetModel, xyzBounds, bounds);
     // Figure out which tree likely works best
     final double xDelta = xyzBounds.getMaximumX() - xyzBounds.getMinimumX();
     final double yDelta = xyzBounds.getMaximumY() - xyzBounds.getMinimumY();
@@ -259,13 +248,13 @@ class GeoComplexPolygon extends GeoBasePolygon {
     // Select the smallest range
     if (xDelta <= yDelta && xDelta <= zDelta) {
       // Drill down in x
-      return !xtree.traverse(intersector, xyzBounds.getMinimumX(), xyzBounds.getMaximumX());
+      return !xTree.traverse(intersector, xyzBounds.getMinimumX(), xyzBounds.getMaximumX());
     } else if (yDelta <= xDelta && yDelta <= zDelta) {
       // Drill down in y
-      return !ytree.traverse(intersector, xyzBounds.getMinimumY(), xyzBounds.getMaximumY());
+      return !yTree.traverse(intersector, xyzBounds.getMinimumY(), xyzBounds.getMaximumY());
     } else if (zDelta <= xDelta && zDelta <= yDelta) {
       // Drill down in z
-      return !ztree.traverse(intersector, xyzBounds.getMinimumZ(), xyzBounds.getMaximumZ());
+      return !zTree.traverse(intersector, xyzBounds.getMinimumZ(), xyzBounds.getMaximumZ());
     }
     return true;
   }
@@ -277,7 +266,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
     for (final Edge startEdge : shapeStartEdges) {
       Edge currentEdge = startEdge;
       while (true) {
-        currentEdge.plane.recordBounds(this.planetModel, currentEdge.startPlane, currentEdge.edgePlane);
+        bounds.addPlane(this.planetModel, currentEdge.plane, currentEdge.startPlane, currentEdge.endPlane);
         currentEdge = currentEdge.next;
         if (currentEdge == startEdge) {
           break;
@@ -576,7 +565,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
     
     public IntersectorEdgeIterator(final Plane plane, final GeoPoint[] notablePoints, final Membership... bounds) {
       this.plane = plane;
-      this notablePoints = notablePoints;
+      this.notablePoints = notablePoints;
       this.bounds = bounds;
     }
     
@@ -596,11 +585,11 @@ class GeoComplexPolygon extends GeoBasePolygon {
     private final Plane belowPlane;
     private final Membership bound1;
     private final Membership bound2;
-    private final GeoPoint thePoint;
+    private final Vector thePoint;
     
     public int crossingCount = 0;
     
-    public CrossingEdgeIterator(final Plane plane, final Membership bound1, final Membership bound2, final GeoPoint thePoint) {
+    public CrossingEdgeIterator(final Plane plane, final Membership bound1, final Membership bound2, final Vector thePoint) {
       this.plane = plane;
       this.abovePlane = new Plane(plane, true);
       this.belowPlane = new Plane(plane, false);
@@ -612,7 +601,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
     @Override
     public boolean matches(final Edge edge) {
       // Early exit if the point is on the edge.
-      if (edge.plane.evaluateIsZero(thePoint) && edge.startPlane.isWithin(thePoint) && edge.endPlane.isWithin(thePoint)) {
+      if (thePoint != null && edge.plane.evaluateIsZero(thePoint) && edge.startPlane.isWithin(thePoint) && edge.endPlane.isWithin(thePoint)) {
         return false;
       }
       final GeoPoint[] crossingPoints = plane.findCrossings(planetModel, edge.plane, bound1, bound2, edge.startPlane, edge.endPlane);
@@ -637,6 +626,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
         
         if (aboveIntersections.length == 0 && belowIntersections.length == 0) {
           return;
+        }
 
         final boolean edgeCrossesAbove = aboveIntersections.length > 0;
 
@@ -698,6 +688,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
         
         if (aboveIntersections.length == 0 && belowIntersections.length == 0) {
           return;
+        }
 
         final boolean edgeCrossesAbove = aboveIntersections.length > 0;
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/641d221c/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Plane.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Plane.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Plane.java
index 66d093b..2408052 100755
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Plane.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Plane.java
@@ -101,15 +101,7 @@ public class Plane extends Vector {
    *   or false in the negative direction.
    */
   public Plane(final Plane basePlane, final boolean above) {
-    this(basePlane.x, basePlane.y, basePlane.z, outsideEnvelope(basePlane.D, above));
-  }
-  
-  private double outsideEnvelope(final double value, boolean above) {
-    if (above) {
-      return Math.nextUp(value + MINIMUM_RESOLUTION);
-    } else {
-      return Math.nextDown(value - MINIMUM_RESOLUTION);
-    }
+    this(basePlane.x, basePlane.y, basePlane.z, above?Math.nextUp(basePlane.D + MINIMUM_RESOLUTION):Math.nextDown(basePlane.D - MINIMUM_RESOLUTION));
   }
   
   /** Construct the most accurate normalized plane through an x-y point and including the Z axis.
@@ -801,7 +793,7 @@ public class Plane extends Vector {
    * @param moreBounds is another set of bounds.
    * @return the intersection point(s) on the ellipsoid, if there are any.
    */
-  protected GeoPoint[] findCrosses(final PlanetModel planetModel, final Plane q, final Membership[] bounds, final Membership[] moreBounds) {
+  protected GeoPoint[] findCrossings(final PlanetModel planetModel, final Plane q, final Membership[] bounds, final Membership[] moreBounds) {
     // This code in this method is very similar to findIntersections(), but eliminates the cases where
     // crossings are detected.
     // Unnormalized, unchecked...


[11/26] lucene-solr:master: Introduce notion of inside/outside for DualCrossing.

Posted by kw...@apache.org.
Introduce notion of inside/outside for DualCrossing.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/f3748eba
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/f3748eba
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/f3748eba

Branch: refs/heads/master
Commit: f3748ebaa4d95bbbf5dd55d1bfdf5e90217365c7
Parents: 42d3f3d
Author: Karl Wright <Da...@gmail.com>
Authored: Tue Apr 26 07:46:44 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Tue Apr 26 07:46:44 2016 -0400

----------------------------------------------------------------------
 .../spatial3d/geom/GeoComplexPolygon.java       | 123 ++++++++++++-------
 1 file changed, 80 insertions(+), 43 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f3748eba/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
index 88db3d6..7e4d01c 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
@@ -715,13 +715,15 @@ class GeoComplexPolygon extends GeoBasePolygon {
     private boolean isSecondLeg = false;
     
     private final Plane testPointPlane;
-    private final Plane testPointAbovePlane;
-    private final Plane testPointBelowPlane;
+    private final Plane testPointInsidePlane;
+    private final Plane testPointOutsidePlane;
     private final Plane travelPlane;
-    private final Plane travelAbovePlane;
-    private final Plane travelBelowPlane;
+    private final Plane travelInsidePlane;
+    private final Plane travelOutsidePlane;
     private final Vector thePoint;
     
+    private final GeoPoint intersectionPoint;
+    
     private final SidedPlane testPointCutoffPlane;
     private final SidedPlane checkPointCutoffPlane;
     private final SidedPlane testPointOtherCutoffPlane;
@@ -732,21 +734,56 @@ class GeoComplexPolygon extends GeoBasePolygon {
     public DualCrossingEdgeIterator(final Plane testPointPlane, final Plane testPointAbovePlane, final Plane testPointBelowPlane,
       final Plane travelPlane, final Vector testPoint, final Vector thePoint) {
       this.testPointPlane = testPointPlane;
-      this.testPointAbovePlane = testPointAbovePlane;
-      this.testPointBelowPlane = testPointBelowPlane;
       this.travelPlane = travelPlane;
       this.thePoint = thePoint;
-      this.travelAbovePlane = new Plane(travelPlane, true);
-      this.travelBelowPlane = new Plane(travelPlane, false);
       this.testPointCutoffPlane = new SidedPlane(thePoint, testPointPlane, testPoint);
       this.checkPointCutoffPlane = new SidedPlane(testPoint, travelPlane, thePoint);
       // Now, find the intersection of the check and test point planes.
       final GeoPoint[] intersectionPoints = travelPlane.findIntersections(planetModel, testPointPlane, testPointCutoffPlane, checkPointCutoffPlane);
       assert intersectionPoints != null : "couldn't find any intersections";
       assert intersectionPoints.length != 1 : "wrong number of intersection points";
-      this.testPointOtherCutoffPlane = new SidedPlane(testPoint, testPointPlane, intersectionPoints[0]);
-      this.checkPointOtherCutoffPlane = new SidedPlane(thePoint, travelPlane, intersectionPoints[0]);
-    
+      this.intersectionPoint = intersectionPoints[0];
+      this.testPointOtherCutoffPlane = new SidedPlane(testPoint, testPointPlane, intersectionPoint);
+      this.checkPointOtherCutoffPlane = new SidedPlane(thePoint, travelPlane, intersectionPoint);
+        
+      // Figure out which of the above/below planes are inside vs. outside.  To do this,
+      // we look for the point that is within the bounds of the testPointPlane and travelPlane.  The two sides that intersected there are the inside
+      // borders.
+      final Plane travelAbovePlane = new Plane(travelPlane, true);
+      final Plane travelBelowPlane = new Plane(travelPlane, false);
+      final GeoPoint[] aboveAbove = travelAbovePlane.findIntersections(planetModel, testPointAbovePlane, testPointCutoffPlane, testPointOtherCutoffPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane);
+      assert aboveAbove != null : "Above + above should not be coplanar";
+      final GeoPoint[] aboveBelow = travelAbovePlane.findIntersections(planetModel, testPointBelowPlane, testPointCutoffPlane, testPointOtherCutoffPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane);
+      assert aboveBelow != null : "Above + below should not be coplanar";
+      final GeoPoint[] belowBelow = travelBelowPlane.findIntersections(planetModel, testPointBelowPlane, testPointCutoffPlane, testPointOtherCutoffPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane);
+      assert belowBelow != null : "Below + below should not be coplanar";
+      final GeoPoint[] belowAbove = travelBelowPlane.findIntersections(planetModel, testPointAbovePlane, testPointCutoffPlane, testPointOtherCutoffPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane);
+      assert belowAbove != null : "Below + above should not be coplanar";
+      
+      assert aboveAbove.length + aboveBelow.length + belowBelow.length + belowAbove.length == 1 : "Can be exactly one inside point";
+      
+      if (aboveAbove.length > 0) {
+        travelInsidePlane = travelAbovePlane;
+        testPointInsidePlane = testPointAbovePlane;
+        travelOutsidePlane = travelBelowPlane;
+        testPointOutsidePlane = testPointBelowPlane;
+      } else if (aboveBelow.length > 0) {
+        travelInsidePlane = travelAbovePlane;
+        testPointInsidePlane = testPointBelowPlane;
+        travelOutsidePlane = travelBelowPlane;
+        testPointOutsidePlane = testPointAbovePlane;
+      } else if (belowBelow.length > 0) {
+        travelInsidePlane = travelBelowPlane;
+        testPointInsidePlane = testPointBelowPlane;
+        travelOutsidePlane = travelAbovePlane;
+        testPointOutsidePlane = testPointAbovePlane;
+      } else {
+        travelInsidePlane = travelBelowPlane;
+        testPointInsidePlane = testPointAbovePlane;
+        travelOutsidePlane = travelAbovePlane;
+        testPointOutsidePlane = testPointBelowPlane;
+      }
+        
     }
 
     public void setSecondLeg() {
@@ -776,20 +813,20 @@ class GeoComplexPolygon extends GeoBasePolygon {
 
     private void countCrossingPoint(final GeoPoint crossingPoint, final Edge edge) {
       final Plane plane;
-      final Plane abovePlane;
-      final Plane belowPlane;
+      final Plane insidePlane;
+      final Plane outsidePlane;
       final SidedPlane bound1;
       final SidedPlane bound2;
       if (isSecondLeg) {
         plane = travelPlane;
-        abovePlane = travelAbovePlane;
-        belowPlane = travelBelowPlane;
+        insidePlane = travelInsidePlane;
+        outsidePlane = travelOutsidePlane;
         bound1 = checkPointCutoffPlane;
         bound2 = checkPointOtherCutoffPlane;
       } else {
         plane = testPointPlane;
-        abovePlane = testPointAbovePlane;
-        belowPlane = testPointBelowPlane;
+        insidePlane = testPointInsidePlane;
+        outsidePlane = testPointOutsidePlane;
         bound1 = testPointCutoffPlane;
         bound2 = testPointOtherCutoffPlane;
       }
@@ -800,29 +837,29 @@ class GeoComplexPolygon extends GeoBasePolygon {
         // We have to figure out if this crossing should be counted.
         
         // Does the crossing for this edge go up, or down?  Or can't we tell?
-        final GeoPoint[] aboveIntersections = abovePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
-        final GeoPoint[] belowIntersections = belowPlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
+        final GeoPoint[] insideIntersections = insidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
+        final GeoPoint[] outsideIntersections = outsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
         
-        assert !(aboveIntersections.length > 0 && belowIntersections.length > 0) : "edge that ends in a crossing can't both up and down";
+        assert !(insideIntersections.length > 0 && outsideIntersections.length > 0) : "edge that ends in a crossing can't both up and down";
         
-        if (aboveIntersections.length == 0 && belowIntersections.length == 0) {
+        if (insideIntersections.length == 0 && outsideIntersections.length == 0) {
           return;
         }
 
-        final boolean edgeCrossesAbove = aboveIntersections.length > 0;
+        final boolean edgeCrossesInside = insideIntersections.length > 0;
 
         // This depends on the previous edge that first departs from identicalness.
         Edge assessEdge = edge;
-        GeoPoint[] assessAboveIntersections;
-        GeoPoint[] assessBelowIntersections;
+        GeoPoint[] assessInsideIntersections;
+        GeoPoint[] assessOutsideIntersections;
         while (true) {
           assessEdge = assessEdge.previous;
-          assessAboveIntersections = abovePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
-          assessBelowIntersections = belowPlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
+          assessInsideIntersections = insidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
+          assessOutsideIntersections = outsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
 
-          assert !(assessAboveIntersections.length > 0 && assessBelowIntersections.length > 0) : "assess edge that ends in a crossing can't both up and down";
+          assert !(assessInsideIntersections.length > 0 && assessOutsideIntersections.length > 0) : "assess edge that ends in a crossing can't both up and down";
 
-          if (assessAboveIntersections.length == 0 && assessBelowIntersections.length == 0) {
+          if (assessInsideIntersections.length == 0 && assessOutsideIntersections.length == 0) {
             continue;
           }
           break;
@@ -853,8 +890,8 @@ class GeoComplexPolygon extends GeoBasePolygon {
         // point where they hit the plane.  This may be complicated by the 3D geometry; it may not be safe just to look at the endpoints of the edges
         // and make an assessment that way, since a single edge can intersect the plane at more than one point.
         
-        final boolean assessEdgeAbove = assessAboveIntersections.length > 0;
-        if (assessEdgeAbove != edgeCrossesAbove) {
+        final boolean assessEdgeInside = assessInsideIntersections.length > 0;
+        if (assessEdgeInside != edgeCrossesInside) {
           crossingCount++;
         }
         
@@ -862,29 +899,29 @@ class GeoComplexPolygon extends GeoBasePolygon {
         // Figure out if the crossing should be counted.
         
         // Does the crossing for this edge go up, or down?  Or can't we tell?
-        final GeoPoint[] aboveIntersections = abovePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
-        final GeoPoint[] belowIntersections = belowPlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
+        final GeoPoint[] insideIntersections = insidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
+        final GeoPoint[] outsideIntersections = outsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
         
-        assert !(aboveIntersections.length > 0 && belowIntersections.length > 0) : "edge that ends in a crossing can't both up and down";
+        assert !(insideIntersections.length > 0 && outsideIntersections.length > 0) : "edge that ends in a crossing can't both up and down";
         
-        if (aboveIntersections.length == 0 && belowIntersections.length == 0) {
+        if (insideIntersections.length == 0 && outsideIntersections.length == 0) {
           return;
         }
 
-        final boolean edgeCrossesAbove = aboveIntersections.length > 0;
+        final boolean edgeCrossesInside = insideIntersections.length > 0;
 
         // This depends on the previous edge that first departs from identicalness.
         Edge assessEdge = edge;
-        GeoPoint[] assessAboveIntersections;
-        GeoPoint[] assessBelowIntersections;
+        GeoPoint[] assessInsideIntersections;
+        GeoPoint[] assessOutsideIntersections;
         while (true) {
           assessEdge = assessEdge.next;
-          assessAboveIntersections = abovePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
-          assessBelowIntersections = belowPlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
+          assessInsideIntersections = insidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
+          assessOutsideIntersections = outsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
 
-          assert !(assessAboveIntersections.length > 0 && assessBelowIntersections.length > 0) : "assess edge that ends in a crossing can't both up and down";
+          assert !(assessInsideIntersections.length > 0 && assessOutsideIntersections.length > 0) : "assess edge that ends in a crossing can't both up and down";
 
-          if (assessAboveIntersections.length == 0 && assessBelowIntersections.length == 0) {
+          if (assessInsideIntersections.length == 0 && assessOutsideIntersections.length == 0) {
             continue;
           }
           break;
@@ -901,8 +938,8 @@ class GeoComplexPolygon extends GeoBasePolygon {
         // point where they hit the plane.  This may be complicated by the 3D geometry; it may not be safe just to look at the endpoints of the edges
         // and make an assessment that way, since a single edge can intersect the plane at more than one point.
 
-        final boolean assessEdgeAbove = assessAboveIntersections.length > 0;
-        if (assessEdgeAbove != edgeCrossesAbove) {
+        final boolean assessEdgeInside = assessInsideIntersections.length > 0;
+        if (assessEdgeInside != edgeCrossesInside) {
           crossingCount++;
         }
 


[05/26] lucene-solr:master: Flesh out logic for handling vertex on plane case.

Posted by kw...@apache.org.
Flesh out logic for handling vertex on plane case.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/282c9a8f
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/282c9a8f
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/282c9a8f

Branch: refs/heads/master
Commit: 282c9a8fecbd327d845eec1f2bb276bd262d293a
Parents: d287eca
Author: Karl Wright <Da...@gmail.com>
Authored: Mon Apr 25 10:15:11 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Mon Apr 25 10:15:11 2016 -0400

----------------------------------------------------------------------
 .../spatial3d/geom/GeoComplexPolygon.java       | 194 ++++++++++++++++++-
 .../org/apache/lucene/spatial3d/geom/Plane.java |  20 +-
 2 files changed, 210 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/282c9a8f/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
index 9ffbcc7..02ce02c 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
@@ -40,7 +40,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
   private final ZTree ztree = new ZTree();
   
   private final boolean testPointInSet;
-  private final Plane testPointVerticalPlane;
+  private final Plane testPointZPlane;
   private final GeoPoint[] edgePoints;
   private final Edge[] shapeStartEdges;
   
@@ -62,7 +62,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
     if (p == null) {
       p = new Plane(1.0, 0.0, 0.0, 0.0);
     }
-    this.testPointVerticalPlane = p;
+    this.testPointZPlane = p;
     this.edgePoints = new GeoPoint[pointsList.size()];
     this.shapeStartEdges = new Edge[pointsList.size()];
     int edgePointIndex = 0;
@@ -109,7 +109,48 @@ class GeoComplexPolygon extends GeoBasePolygon {
 
   @Override
   public boolean isWithin(final double x, final double y, final double z) {
-    // MHL
+    return isWithin(new Vector(x, y, z));
+  }
+  
+  @Override
+  public boolean isWithin(final Vector thePoint) {
+    // Construct a horizontal plane that goes through the provided z value.  This, along with the
+    // testPointZPlane, will provide a way of counting intersections between this point and the test point.
+    // We use z exclusively for this logic at the moment but we may in the future choose x or y based on which
+    // is bigger.
+    if (testPoint.isNumericallyIdentical(thePoint)) {
+      return true;
+    }
+    if (testPointZPlane.evaluateIsZero(thePoint)) {
+      // The point we are assessing goes through only one plane.
+      // Compute cutoff planes
+      final SidedPlane testPointCutoff =  new SidedPlane(thePoint, testPointZPlane, testPoint);
+      final SidedPlane checkPointCutoff = new SidedPlane(testPoint, testPointZPlane, thePoint);
+
+      // Count crossings
+      final CrossingEdgeIterator crossingEdgeIterator = new CrossingEdgeIterator(testPointZPlane, testPointCutoff, checkPointCutoff);
+      
+      // Compute bounds for this part of testZPlane
+      final XYZBounds testZPlaneBounds = new XYZBounds();
+      testPointZPlane.recordBounds(planetModel, testZPlaneBounds, testPointCutoff, checkPointCutoff);
+      
+      // Pick which tree to use
+      final double xDelta = testZPlaneBounds.getMaximumX() - testZPlaneBounds.getMinimumX();
+      final double yDelta = testZPlaneBounds.getMaximumY() - testZPlaneBounds.getMinimumY();
+      if (xDelta <= yDelta) {
+        xTree.traverse(crossingEdgeIterator, testZPlaneBounds.getMinimumX(), testZPlaneBounds.getMaximumX());
+      } else if (yDelta <= xDelta) {
+        yTree.traverse(crossingEdgeIterator, testZPlaneBounds.getMinimumY(), testZPlaneBounds.getMaximumY());
+      }
+      return ((crossingEdgeIterator.getCrossingCount() & 0x00000001) == 0)?testPointInSet:!testPointInSet;
+    } else {
+      // The point requires two planes
+      final Plane xyPlane = new Plane(planetModel, z);
+      // We need to plan a path from the test point to the point to be evaluated.
+      // This requires finding the intersection point between the xyPlane and the testPointZPlane
+      // that is closest to our point.
+      // MHL
+    }
     return false;
   }
   
@@ -460,6 +501,153 @@ class GeoComplexPolygon extends GeoBasePolygon {
 
   }
   
+  /** Count the number of verifiable edge crossings.
+   */
+  private class CrossingEdgeIterator implements EdgeIterator {
+    
+    private final Plane plane;
+    private final Plane abovePlane;
+    private final Plane belowPlane;
+    private final Membership bound1;
+    private final Membership bound2;
+    
+    public int crossingCount = 0;
+    
+    public CrossingEdgeIterator(final Plane plane, final Membership bound1, final Membership bound2) {
+      this.plane = plane;
+      this.abovePlane = new Plane(plane, true);
+      this.belowPlane = new Plane(plane, false);
+      this.bound1 = bound1;
+      this.bound2 = bound2;
+    }
+    
+    @Override
+    public boolean matches(final Edge edge) {
+      final GeoPoint[] crossingPoints = plane.findCrossings(planetModel, edge.plane, bound1, bound2, edge.startPlane, edge.endPlane);
+      if (crossingPoints != null) {
+        // We need to handle the endpoint case, which is quite tricky.
+        for (final GeoPoint crossingPoint : crossingPoints) {
+          countCrossingPoint(crossingPoint, plane, edge);
+        }
+      }
+      return true;
+    }
+
+    private void countCrossingPoint(final GeoPoint crossingPoint, final Plane plane, final Edge edge) {
+      if (crossingPoint.isNumericallyIdentical(edge.startPoint)) {
+        // We have to figure out if this crossing should be counted.
+        
+        // Does the crossing for this edge go up, or down?  Or can't we tell?
+        final GeoPoint[] aboveIntersections = abovePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
+        final GeoPoint[] belowIntersections = belowPlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
+        
+        assert !(aboveIntersections.length > 0 && belowIntersections.length > 0) : "edge that ends in a crossing can't both up and down";
+        
+        if (aboveIntersections.length == 0 && belowIntersections.length == 0) {
+          return;
+
+        final boolean edgeCrossesAbove = aboveIntersections.length > 0;
+
+        // This depends on the previous edge that first departs from identicalness.
+        Edge assessEdge = edge;
+        GeoPoint[] assessAboveIntersections;
+        GeoPoint[] assessBelowIntersections;
+        while (true) {
+          assessEdge = assessEdge.previous;
+          assessAboveIntersections = abovePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
+          assessBelowIntersections = belowPlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
+
+          assert !(assessAboveIntersections.length > 0 && assessBelowIntersections.length > 0) : "assess edge that ends in a crossing can't both up and down";
+
+          if (assessAboveIntersections.length == 0 && assessBelowIntersections.length == 0) {
+            continue;
+          }
+          break;
+        }
+        
+        // Basically, we now want to assess whether both edges that come together at this endpoint leave the plane in opposite
+        // directions.  If they do, then we should count it as a crossing; if not, we should not.  We also have to remember that
+        // each edge we look at can also be looked at again if it, too, seems to cross the plane.
+        
+        // To handle the latter situation, we need to know if the other edge will be looked at also, and then we can make
+        // a decision whether to count or not based on that.
+        
+        // Compute the crossing points of this other edge.
+        final GeoPoint[] otherCrossingPoints = plane.findCrossings(planetModel, assessEdge.plane, bound1, bound2, assessEdge.startPlane, assessEdge.endPlane);
+        
+        // Look for a matching endpoint.  If the other endpoint doesn't show up, it is either out of bounds (in which case the
+        // transition won't be counted for that edge), or it is not a crossing for that edge (so, same conclusion).
+        for (final GeoPoint otherCrossingPoint : otherCrossingPoints) {
+          if (otherCrossingPoint.isNumericallyIdentical(assessEdge.endPoint)) {
+            // Found it!
+            // Both edges will try to contribute to the crossing count.  By convention, we'll only include the earlier one.
+            // Since we're the latter point, we exit here in that case.
+            return;
+          }
+        }
+        
+        // Both edges will not count the same point, so we can proceed.  We need to determine the direction of both edges at the
+        // point where they hit the plane.  This may be complicated by the 3D geometry; it may not be safe just to look at the endpoints of the edges
+        // and make an assessment that way, since a single edge can intersect the plane at more than one point.
+        
+        final boolean assessEdgeAbove = assessAboveIntersections.length > 0;
+        if (assessEdgeAbove != edgeCrossesAbove) {
+          crossingCount++;
+        }
+        
+      } else if (crossingPoint.isNumericallyIdentical(edge.endPoint)) {
+        // Figure out if the crossing should be counted.
+        
+        // Does the crossing for this edge go up, or down?  Or can't we tell?
+        final GeoPoint[] aboveIntersections = abovePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
+        final GeoPoint[] belowIntersections = belowPlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
+        
+        assert !(aboveIntersections.length > 0 && belowIntersections.length > 0) : "edge that ends in a crossing can't both up and down";
+        
+        if (aboveIntersections.length == 0 && belowIntersections.length == 0) {
+          return;
+
+        final boolean edgeCrossesAbove = aboveIntersections.length > 0;
+
+        // This depends on the previous edge that first departs from identicalness.
+        Edge assessEdge = edge;
+        GeoPoint[] assessAboveIntersections;
+        GeoPoint[] assessBelowIntersections;
+        while (true) {
+          assessEdge = assessEdge.next;
+          assessAboveIntersections = abovePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
+          assessBelowIntersections = belowPlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
+
+          assert !(assessAboveIntersections.length > 0 && assessBelowIntersections.length > 0) : "assess edge that ends in a crossing can't both up and down";
+
+          if (assessAboveIntersections.length == 0 && assessBelowIntersections.length == 0) {
+            continue;
+          }
+          break;
+        }
+        
+        // Basically, we now want to assess whether both edges that come together at this endpoint leave the plane in opposite
+        // directions.  If they do, then we should count it as a crossing; if not, we should not.  We also have to remember that
+        // each edge we look at can also be looked at again if it, too, seems to cross the plane.
+        
+        // By definition, we're the earlier plane in this case, so any crossing we detect we must count, by convention.  It is unnecessary
+        // to consider what the other edge does, because when we get to it, it will look back and figure out what we did for this one.
+        
+        // We need to determine the direction of both edges at the
+        // point where they hit the plane.  This may be complicated by the 3D geometry; it may not be safe just to look at the endpoints of the edges
+        // and make an assessment that way, since a single edge can intersect the plane at more than one point.
+
+        final boolean assessEdgeAbove = assessAboveIntersections.length > 0;
+        if (assessEdgeAbove != edgeCrossesAbove) {
+          crossingCount++;
+        }
+
+      } else {
+        crossingCount++;
+      }
+    }
+  }
+  
   @Override
   public boolean equals(Object o) {
     // MHL

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/282c9a8f/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Plane.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Plane.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Plane.java
index a205ca9..66d093b 100755
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Plane.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Plane.java
@@ -94,6 +94,24 @@ public class Plane extends Vector {
     this.D = D;
   }
 
+  /** Construct a plane that is parallel to the one provided, but which is just barely numerically
+   * distinguishable from it, in the direction desired.
+   * @param basePlane is the starting plane.
+   * @param above is set to true if the desired plane is in the positive direction from the base plane,
+   *   or false in the negative direction.
+   */
+  public Plane(final Plane basePlane, final boolean above) {
+    this(basePlane.x, basePlane.y, basePlane.z, outsideEnvelope(basePlane.D, above));
+  }
+  
+  private double outsideEnvelope(final double value, boolean above) {
+    if (above) {
+      return Math.nextUp(value + MINIMUM_RESOLUTION);
+    } else {
+      return Math.nextDown(value - MINIMUM_RESOLUTION);
+    }
+  }
+  
   /** Construct the most accurate normalized plane through an x-y point and including the Z axis.
    * If none of the points can determine the plane, return null.
    * @param planePoints is a set of points to choose from.  The best one for constructing the most precise plane is picked.
@@ -191,7 +209,7 @@ public class Plane extends Vector {
     final double denom = 1.0 / Math.sqrt(y*y + z*z);
     return new Plane(0.0, z * denom, -y * denom, DValue);
   }
-  
+
   /**
    * Evaluate the plane equation for a given point, as represented
    * by a vector.


[26/26] lucene-solr:master: Merge branch 'LUCENE-7241'

Posted by kw...@apache.org.
Merge branch 'LUCENE-7241'


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/7bc50ec1
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/7bc50ec1
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/7bc50ec1

Branch: refs/heads/master
Commit: 7bc50ec1ee0333d2a294ad163ed0f4f3a9c453b6
Parents: 791d1e7 7d33e77
Author: Karl Wright <Da...@gmail.com>
Authored: Thu Apr 28 20:00:19 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Thu Apr 28 20:00:19 2016 -0400

----------------------------------------------------------------------
 .../spatial3d/geom/GeoComplexPolygon.java       | 1230 ++++++++++++++++++
 .../spatial3d/geom/GeoPolygonFactory.java       |  135 +-
 .../org/apache/lucene/spatial3d/geom/Plane.java |  151 ++-
 .../lucene/spatial3d/geom/GeoPolygonTest.java   |   91 +-
 4 files changed, 1594 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7bc50ec1/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java
----------------------------------------------------------------------


[15/26] lucene-solr:master: Finish handling for intersection point

Posted by kw...@apache.org.
Finish handling for intersection point


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/7a4c0774
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/7a4c0774
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/7a4c0774

Branch: refs/heads/master
Commit: 7a4c07745475e41759db9507c3a40170cfdc851b
Parents: 117f79d
Author: Karl Wright <Da...@gmail.com>
Authored: Wed Apr 27 07:30:07 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Wed Apr 27 07:30:07 2016 -0400

----------------------------------------------------------------------
 .../spatial3d/geom/GeoComplexPolygon.java       | 258 ++++++++++---------
 1 file changed, 134 insertions(+), 124 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7a4c0774/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
index b5c29d6..9b0fd1f 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
@@ -834,158 +834,168 @@ class GeoComplexPolygon extends GeoBasePolygon {
       // (a) both inside edges are considered together at all times;
       // (b) both outside edges are considered together at all times;
       // (c) inside edge crossings that are between the other leg's inside and outside edge are ignored.
-      if (crossingPoint.isNumericallyIdentical(intersectionPoint)) {
-        // Intersection point crossing
-        
-        // MHL to deal with intersection point crossing!!
-        
-      } else {
-        // Standard plane crossing, either first leg or second leg
       
-        final Plane plane;
-        final Plane insidePlane;
-        final Plane outsidePlane;
-        final SidedPlane bound1;
-        final SidedPlane bound2;
+      // Intersection point crossings are either simple, or a crossing on an endpoint.
+      // In either case, we have to be sure to count each edge only once, since it might appear in both the
+      // first leg and the second.  If the first leg can process it, it should, and the second should skip it.
+      if (crossingPoint.isNumericallyIdentical(intersectionPoint)) {
         if (isSecondLeg) {
-          plane = travelPlane;
-          insidePlane = travelInsidePlane;
-          outsidePlane = travelOutsidePlane;
-          bound1 = checkPointCutoffPlane;
-          bound2 = checkPointOtherCutoffPlane;
-        } else {
-          plane = testPointPlane;
-          insidePlane = testPointInsidePlane;
-          outsidePlane = testPointOutsidePlane;
-          bound1 = testPointCutoffPlane;
-          bound2 = testPointOtherCutoffPlane;
+          // See whether this edge would have been processed in the first leg; if so, we skip it.
+          final GeoPoint[] firstLegCrossings = testPointPlane.findCrossings(planetModel, edge.plane, testPointCutoffPlane, testPointOtherCutoffPlane, edge.startPlane, edge.endPlane);
+          for (final GeoPoint firstLegCrossing : firstLegCrossings) {
+            if (firstLegCrossing.isNumericallyIdentical(intersectionPoint)) {
+              // We already processed it, so we're done here.
+              return;
+            }
+          }
         }
+      }
+        
+      // Plane crossing, either first leg or second leg
+      
+      final Plane plane;
+      final Plane insidePlane;
+      final Plane outsidePlane;
+      final SidedPlane bound1;
+      final SidedPlane bound2;
+      if (isSecondLeg) {
+        plane = travelPlane;
+        insidePlane = travelInsidePlane;
+        outsidePlane = travelOutsidePlane;
+        bound1 = checkPointCutoffPlane;
+        bound2 = checkPointOtherCutoffPlane;
+      } else {
+        plane = testPointPlane;
+        insidePlane = testPointInsidePlane;
+        outsidePlane = testPointOutsidePlane;
+        bound1 = testPointCutoffPlane;
+        bound2 = testPointOtherCutoffPlane;
+      }
         
-        if (crossingPoint.isNumericallyIdentical(edge.startPoint)) {
-          // We have to figure out if this crossing should be counted.
+      if (crossingPoint.isNumericallyIdentical(edge.startPoint)) {
+        // We have to figure out if this crossing should be counted.
           
-          // Does the crossing for this edge go up, or down?  Or can't we tell?
-          final GeoPoint[] insideTestPointPlaneIntersections = testPointInsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane, insideTestPointCutoffPlane);
-          final GeoPoint[] insideTravelPlaneIntersections = travelInsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane, insideTravelCutoffPlane);
-          final GeoPoint[] outsideTestPointPlaneIntersections = testPointOutsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
-          final GeoPoint[] outsideTravelPlaneIntersections = travelOutsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
+        // Does the crossing for this edge go up, or down?  Or can't we tell?
+        final GeoPoint[] insideTestPointPlaneIntersections = testPointInsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane, insideTestPointCutoffPlane);
+        final GeoPoint[] insideTravelPlaneIntersections = travelInsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane, insideTravelCutoffPlane);
+        final GeoPoint[] outsideTestPointPlaneIntersections = testPointOutsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
+        final GeoPoint[] outsideTravelPlaneIntersections = travelOutsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
           
-          assert !(insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length > 0 && outsideTestPointPlaneIntersections.length + outsideTravelPlaneIntersections.length > 0) : "edge that ends in a crossing can't both up and down";
+        assert !(insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length > 0 && outsideTestPointPlaneIntersections.length + outsideTravelPlaneIntersections.length > 0) : "edge that ends in a crossing can't both up and down";
           
-          if (insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length == 0 && outsideTestPointPlaneIntersections.length + outsideTravelPlaneIntersections.length == 0) {
-            return;
-          }
+        if (insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length == 0 && outsideTestPointPlaneIntersections.length + outsideTravelPlaneIntersections.length == 0) {
+          return;
+        }
 
-          final boolean edgeCrossesInside = insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length > 0;
-
-          // This depends on the previous edge that first departs from identicalness.
-          Edge assessEdge = edge;
-          GeoPoint[] assessInsideTestPointIntersections;
-          GeoPoint[] assessInsideTravelIntersections;
-          GeoPoint[] assessOutsideTestPointIntersections;
-          GeoPoint[] assessOutsideTravelIntersections;
-          while (true) {
-            assessEdge = assessEdge.previous;
-            assessInsideTestPointIntersections = testPointInsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane, insideTestPointCutoffPlane);
-            assessInsideTravelIntersections = travelInsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane, insideTravelCutoffPlane);
-            assessOutsideTestPointIntersections = testPointOutsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
-            assessOutsideTravelIntersections = travelOutsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
-
-            assert !(assessInsideTestPointIntersections.length + assessInsideTravelIntersections.length > 0 && assessOutsideTestPointIntersections.length + assessOutsideTravelIntersections.length > 0) : "assess edge that ends in a crossing can't both up and down";
-
-            if (assessInsideTestPointIntersections.length + assessInsideTravelIntersections.length == 0 && assessOutsideTestPointIntersections.length + assessOutsideTravelIntersections.length == 0) {
-              continue;
-            }
-            break;
+        final boolean edgeCrossesInside = insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length > 0;
+
+        // This depends on the previous edge that first departs from identicalness.
+        Edge assessEdge = edge;
+        GeoPoint[] assessInsideTestPointIntersections;
+        GeoPoint[] assessInsideTravelIntersections;
+        GeoPoint[] assessOutsideTestPointIntersections;
+        GeoPoint[] assessOutsideTravelIntersections;
+        while (true) {
+          assessEdge = assessEdge.previous;
+          assessInsideTestPointIntersections = testPointInsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane, insideTestPointCutoffPlane);
+          assessInsideTravelIntersections = travelInsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane, insideTravelCutoffPlane);
+          assessOutsideTestPointIntersections = testPointOutsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
+          assessOutsideTravelIntersections = travelOutsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
+
+          assert !(assessInsideTestPointIntersections.length + assessInsideTravelIntersections.length > 0 && assessOutsideTestPointIntersections.length + assessOutsideTravelIntersections.length > 0) : "assess edge that ends in a crossing can't both up and down";
+
+          if (assessInsideTestPointIntersections.length + assessInsideTravelIntersections.length == 0 && assessOutsideTestPointIntersections.length + assessOutsideTravelIntersections.length == 0) {
+            continue;
           }
+          break;
+        }
 
-          // Basically, we now want to assess whether both edges that come together at this endpoint leave the plane in opposite
-          // directions.  If they do, then we should count it as a crossing; if not, we should not.  We also have to remember that
-          // each edge we look at can also be looked at again if it, too, seems to cross the plane.
+        // Basically, we now want to assess whether both edges that come together at this endpoint leave the plane in opposite
+        // directions.  If they do, then we should count it as a crossing; if not, we should not.  We also have to remember that
+        // each edge we look at can also be looked at again if it, too, seems to cross the plane.
           
-          // To handle the latter situation, we need to know if the other edge will be looked at also, and then we can make
-          // a decision whether to count or not based on that.
+        // To handle the latter situation, we need to know if the other edge will be looked at also, and then we can make
+        // a decision whether to count or not based on that.
           
-          // Compute the crossing points of this other edge.
-          final GeoPoint[] otherCrossingPoints = plane.findCrossings(planetModel, assessEdge.plane, bound1, bound2, assessEdge.startPlane, assessEdge.endPlane);
+        // Compute the crossing points of this other edge.
+        final GeoPoint[] otherCrossingPoints = plane.findCrossings(planetModel, assessEdge.plane, bound1, bound2, assessEdge.startPlane, assessEdge.endPlane);
           
-          // Look for a matching endpoint.  If the other endpoint doesn't show up, it is either out of bounds (in which case the
-          // transition won't be counted for that edge), or it is not a crossing for that edge (so, same conclusion).
-          for (final GeoPoint otherCrossingPoint : otherCrossingPoints) {
-            if (otherCrossingPoint.isNumericallyIdentical(assessEdge.endPoint)) {
-              // Found it!
-              // Both edges will try to contribute to the crossing count.  By convention, we'll only include the earlier one.
-              // Since we're the latter point, we exit here in that case.
-              return;
-            }
+        // Look for a matching endpoint.  If the other endpoint doesn't show up, it is either out of bounds (in which case the
+        // transition won't be counted for that edge), or it is not a crossing for that edge (so, same conclusion).
+        for (final GeoPoint otherCrossingPoint : otherCrossingPoints) {
+          if (otherCrossingPoint.isNumericallyIdentical(assessEdge.endPoint)) {
+            // Found it!
+            // Both edges will try to contribute to the crossing count.  By convention, we'll only include the earlier one.
+            // Since we're the latter point, we exit here in that case.
+            return;
           }
+        }
           
-          // Both edges will not count the same point, so we can proceed.  We need to determine the direction of both edges at the
-          // point where they hit the plane.  This may be complicated by the 3D geometry; it may not be safe just to look at the endpoints of the edges
-          // and make an assessment that way, since a single edge can intersect the plane at more than one point.
+        // Both edges will not count the same point, so we can proceed.  We need to determine the direction of both edges at the
+        // point where they hit the plane.  This may be complicated by the 3D geometry; it may not be safe just to look at the endpoints of the edges
+        // and make an assessment that way, since a single edge can intersect the plane at more than one point.
           
-          final boolean assessEdgeInside = assessInsideTestPointIntersections.length + assessInsideTravelIntersections.length > 0;
-          if (assessEdgeInside != edgeCrossesInside) {
-            crossingCount++;
-          }
+        final boolean assessEdgeInside = assessInsideTestPointIntersections.length + assessInsideTravelIntersections.length > 0;
+        if (assessEdgeInside != edgeCrossesInside) {
+          crossingCount++;
+        }
           
-        } else if (crossingPoint.isNumericallyIdentical(edge.endPoint)) {
-          // Figure out if the crossing should be counted.
+      } else if (crossingPoint.isNumericallyIdentical(edge.endPoint)) {
+        // Figure out if the crossing should be counted.
           
-          // Does the crossing for this edge go up, or down?  Or can't we tell?
-          final GeoPoint[] insideTestPointPlaneIntersections = testPointInsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane, insideTestPointCutoffPlane);
-          final GeoPoint[] insideTravelPlaneIntersections = travelInsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane, insideTravelCutoffPlane);
-          final GeoPoint[] outsideTestPointPlaneIntersections = testPointOutsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
-          final GeoPoint[] outsideTravelPlaneIntersections = travelOutsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
+        // Does the crossing for this edge go up, or down?  Or can't we tell?
+        final GeoPoint[] insideTestPointPlaneIntersections = testPointInsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane, insideTestPointCutoffPlane);
+        final GeoPoint[] insideTravelPlaneIntersections = travelInsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane, insideTravelCutoffPlane);
+        final GeoPoint[] outsideTestPointPlaneIntersections = testPointOutsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
+        final GeoPoint[] outsideTravelPlaneIntersections = travelOutsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
           
-          assert !(insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length > 0 && outsideTestPointPlaneIntersections.length + outsideTravelPlaneIntersections.length > 0) : "edge that ends in a crossing can't both up and down";
+        assert !(insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length > 0 && outsideTestPointPlaneIntersections.length + outsideTravelPlaneIntersections.length > 0) : "edge that ends in a crossing can't both up and down";
           
-          if (insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length == 0 && outsideTestPointPlaneIntersections.length + outsideTravelPlaneIntersections.length == 0) {
-            return;
-          }
+        if (insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length == 0 && outsideTestPointPlaneIntersections.length + outsideTravelPlaneIntersections.length == 0) {
+          return;
+        }
 
-          final boolean edgeCrossesInside = insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length > 0;
-
-          // This depends on the previous edge that first departs from identicalness.
-          Edge assessEdge = edge;
-          GeoPoint[] assessInsideTestPointIntersections;
-          GeoPoint[] assessInsideTravelIntersections;
-          GeoPoint[] assessOutsideTestPointIntersections;
-          GeoPoint[] assessOutsideTravelIntersections;
-          while (true) {
-            assessEdge = assessEdge.next;
-            assessInsideTestPointIntersections = testPointInsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane, insideTestPointCutoffPlane);
-            assessInsideTravelIntersections = travelInsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane, insideTravelCutoffPlane);
-            assessOutsideTestPointIntersections = testPointOutsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
-            assessOutsideTravelIntersections = travelOutsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
-
-            assert !(assessInsideTestPointIntersections.length + assessInsideTravelIntersections.length > 0 && assessOutsideTestPointIntersections.length + assessOutsideTravelIntersections.length > 0) : "assess edge that ends in a crossing can't both up and down";
-
-            if (assessInsideTestPointIntersections.length + assessInsideTravelIntersections.length == 0 && assessOutsideTestPointIntersections.length + assessOutsideTravelIntersections.length == 0) {
-              continue;
-            }
-            break;
+        final boolean edgeCrossesInside = insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length > 0;
+
+        // This depends on the previous edge that first departs from identicalness.
+        Edge assessEdge = edge;
+        GeoPoint[] assessInsideTestPointIntersections;
+        GeoPoint[] assessInsideTravelIntersections;
+        GeoPoint[] assessOutsideTestPointIntersections;
+        GeoPoint[] assessOutsideTravelIntersections;
+        while (true) {
+          assessEdge = assessEdge.next;
+          assessInsideTestPointIntersections = testPointInsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane, insideTestPointCutoffPlane);
+          assessInsideTravelIntersections = travelInsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane, insideTravelCutoffPlane);
+          assessOutsideTestPointIntersections = testPointOutsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
+          assessOutsideTravelIntersections = travelOutsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
+
+          assert !(assessInsideTestPointIntersections.length + assessInsideTravelIntersections.length > 0 && assessOutsideTestPointIntersections.length + assessOutsideTravelIntersections.length > 0) : "assess edge that ends in a crossing can't both up and down";
+
+          if (assessInsideTestPointIntersections.length + assessInsideTravelIntersections.length == 0 && assessOutsideTestPointIntersections.length + assessOutsideTravelIntersections.length == 0) {
+            continue;
           }
+          break;
+        }
           
-          // Basically, we now want to assess whether both edges that come together at this endpoint leave the plane in opposite
-          // directions.  If they do, then we should count it as a crossing; if not, we should not.  We also have to remember that
-          // each edge we look at can also be looked at again if it, too, seems to cross the plane.
+        // Basically, we now want to assess whether both edges that come together at this endpoint leave the plane in opposite
+        // directions.  If they do, then we should count it as a crossing; if not, we should not.  We also have to remember that
+        // each edge we look at can also be looked at again if it, too, seems to cross the plane.
           
-          // By definition, we're the earlier plane in this case, so any crossing we detect we must count, by convention.  It is unnecessary
-          // to consider what the other edge does, because when we get to it, it will look back and figure out what we did for this one.
+        // By definition, we're the earlier plane in this case, so any crossing we detect we must count, by convention.  It is unnecessary
+        // to consider what the other edge does, because when we get to it, it will look back and figure out what we did for this one.
           
-          // We need to determine the direction of both edges at the
-          // point where they hit the plane.  This may be complicated by the 3D geometry; it may not be safe just to look at the endpoints of the edges
-          // and make an assessment that way, since a single edge can intersect the plane at more than one point.
+        // We need to determine the direction of both edges at the
+        // point where they hit the plane.  This may be complicated by the 3D geometry; it may not be safe just to look at the endpoints of the edges
+        // and make an assessment that way, since a single edge can intersect the plane at more than one point.
 
-          final boolean assessEdgeInside = assessInsideTestPointIntersections.length + assessInsideTravelIntersections.length > 0;
-          if (assessEdgeInside != edgeCrossesInside) {
-            crossingCount++;
-          }
-        } else {
-          // Not a special case, so we can safely count a crossing.
+        final boolean assessEdgeInside = assessInsideTestPointIntersections.length + assessInsideTravelIntersections.length > 0;
+        if (assessEdgeInside != edgeCrossesInside) {
           crossingCount++;
         }
+      } else {
+        // Not a special case, so we can safely count a crossing.
+        crossingCount++;
       }
     }
   }


[20/26] lucene-solr:master: Rewrite tree handling to actually work.

Posted by kw...@apache.org.
Rewrite tree handling to actually work.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/d990d975
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/d990d975
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/d990d975

Branch: refs/heads/master
Commit: d990d975ac239bfa9802196287cf2c1fe0194f07
Parents: 86be6a6
Author: Karl Wright <Da...@gmail.com>
Authored: Thu Apr 28 08:12:42 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Thu Apr 28 08:12:42 2016 -0400

----------------------------------------------------------------------
 .../spatial3d/geom/GeoComplexPolygon.java       | 382 ++++++++++---------
 1 file changed, 202 insertions(+), 180 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d990d975/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
index 06d6dba..3eff223 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
@@ -16,11 +16,9 @@
  */
 package org.apache.lucene.spatial3d.geom;
 
-import java.util.ArrayList;
-import java.util.BitSet;
 import java.util.List;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.Set;
+import java.util.HashSet;
 
 /**
  * GeoComplexPolygon objects are structures designed to handle very large numbers of edges.
@@ -130,7 +128,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
       // Use the XZ plane exclusively.
       final LinearCrossingEdgeIterator crossingEdgeIterator = new LinearCrossingEdgeIterator(testPointXZPlane, testPointXZAbovePlane, testPointXZBelowPlane, testPoint, thePoint);
       // Traverse our way from the test point to the check point.  Use the y tree because that's fixed.
-      if (!yTree.traverse(crossingEdgeIterator, testPoint.y, testPoint.y)) {
+      if (!yTree.traverse(crossingEdgeIterator, testPoint.y)) {
         // Endpoint is on edge
         return true;
       }
@@ -139,7 +137,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
       // Use the YZ plane exclusively.
       final LinearCrossingEdgeIterator crossingEdgeIterator = new LinearCrossingEdgeIterator(testPointYZPlane, testPointYZAbovePlane, testPointYZBelowPlane, testPoint, thePoint);
       // Traverse our way from the test point to the check point.  Use the x tree because that's fixed.
-      if (!xTree.traverse(crossingEdgeIterator, testPoint.x, testPoint.x)) {
+      if (!xTree.traverse(crossingEdgeIterator, testPoint.x)) {
         // Endpoint is on edge
         return true;
       }
@@ -148,7 +146,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
       // Use the XY plane exclusively.
       final LinearCrossingEdgeIterator crossingEdgeIterator = new LinearCrossingEdgeIterator(testPointXYPlane, testPointXYAbovePlane, testPointXYBelowPlane, testPoint, thePoint);
       // Traverse our way from the test point to the check point.  Use the z tree because that's fixed.
-      if (!zTree.traverse(crossingEdgeIterator, testPoint.z, testPoint.z)) {
+      if (!zTree.traverse(crossingEdgeIterator, testPoint.z)) {
         // Endpoint is on edge
         return true;
       }
@@ -280,11 +278,11 @@ class GeoComplexPolygon extends GeoBasePolygon {
       assert bestDistance < Double.MAX_VALUE : "Couldn't find an intersection point of any kind";
       
       final DualCrossingEdgeIterator edgeIterator = new DualCrossingEdgeIterator(firstLegPlane, firstLegAbovePlane, firstLegBelowPlane, secondLegPlane, testPoint, thePoint, intersectionPoint);
-      if (!firstLegTree.traverse(edgeIterator, firstLegValue, firstLegValue)) {
+      if (!firstLegTree.traverse(edgeIterator, firstLegValue)) {
         return true;
       }
       edgeIterator.setSecondLeg();
-      if (!secondLegTree.traverse(edgeIterator, secondLegValue, secondLegValue)) {
+      if (!secondLegTree.traverse(edgeIterator, secondLegValue)) {
         return true;
       }
       return ((edgeIterator.crossingCount  & 1) == 0)?testPointInSet:!testPointInSet;
@@ -304,6 +302,9 @@ class GeoComplexPolygon extends GeoBasePolygon {
     // First, compute the bounds for the the plane
     final XYZBounds xyzBounds = new XYZBounds();
     p.recordBounds(planetModel, xyzBounds, bounds);
+    for (final GeoPoint point : notablePoints) {
+      xyzBounds.addPoint(point);
+    }
     // Figure out which tree likely works best
     final double xDelta = xyzBounds.getMaximumX() - xyzBounds.getMinimumX();
     final double yDelta = xyzBounds.getMaximumY() - xyzBounds.getMinimumY();
@@ -387,6 +388,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
       this.planeBounds.addPoint(startPoint);
       this.planeBounds.addPoint(endPoint);
       this.plane.recordBounds(pm, this.planeBounds, this.startPlane, this.endPlane);
+      System.err.println("Recording edge from "+startPoint+" to "+endPoint+"; bounds = "+planeBounds);
     }
   }
   
@@ -404,39 +406,6 @@ class GeoComplexPolygon extends GeoBasePolygon {
   }
   
   /**
-   * Comparison interface for tree traversal.  An object implementing this interface
-   * gets to decide the relationship between the Edge object and the criteria being considered.
-   */
-  private static interface TraverseComparator {
-    
-    /**
-     * Compare an edge.
-     * @param edge is the edge to compare.
-     * @param minValue is the minimum value to compare (bottom of the range)
-     * @param maxValue is the maximum value to compare (top of the range)
-     * @return -1 if "less" than this one, 0 if overlaps, or 1 if "greater".
-     */
-    public int compare(final Edge edge, final double minValue, final double maxValue);
-    
-  }
-
-  /**
-   * Comparison interface for tree addition.  An object implementing this interface
-   * gets to decide the relationship between the Edge object and the criteria being considered.
-   */
-  private static interface AddComparator {
-    
-    /**
-     * Compare an edge.
-     * @param edge is the edge to compare.
-     * @param addEdge is the edge being added.
-     * @return -1 if "less" than this one, 0 if overlaps, or 1 if "greater".
-     */
-    public int compare(final Edge edge, final Edge addEdge);
-    
-  }
-  
-  /**
    * An instance of this class represents a node in a tree.  The tree is designed to be given
    * a value and from that to iterate over a list of edges.
    * In order to do this efficiently, each new edge is dropped into the tree using its minimum and
@@ -448,207 +417,249 @@ class GeoComplexPolygon extends GeoBasePolygon {
    *
    */
   private static class Node {
+    public final double minimumValue;
+    public final double maximumValue;
     public final Edge edge;
     public Node lesser = null;
     public Node greater = null;
-    public Node overlaps = null;
+    public Node within = null;
     
-    public Node(final Edge edge) {
+    public Node(final Edge edge, final double minimumValue, final double maximumValue) {
       this.edge = edge;
+      this.minimumValue = minimumValue;
+      this.maximumValue = maximumValue;
     }
     
-    public void add(final Edge newEdge, final AddComparator edgeComparator) {
-      Node currentNode = this;
-      while (true) {
-        final int result = edgeComparator.compare(currentNode.edge, newEdge);
-        if (result < 0) {
-          if (currentNode.lesser == null) {
-            currentNode.lesser = new Node(newEdge);
-            return;
-          }
-          currentNode = currentNode.lesser;
-        } else if (result > 0) {
-          if (currentNode.greater == null) {
-            currentNode.greater = new Node(newEdge);
-            return;
-          }
-          currentNode = currentNode.greater;
-        } else {
-          if (currentNode.overlaps == null) {
-            currentNode.overlaps = new Node(newEdge);
-            return;
-          }
-          currentNode = currentNode.overlaps;
-        }
+  }
+  
+  /** An interface describing a tree.
+   */
+  private static abstract class Tree {
+    private Node rootNode = null;
+    
+    protected static final int CONTAINED = 0;
+    protected static final int WITHIN = 1;
+    protected static final int OVERLAPS_MINIMUM = 2;
+    protected static final int OVERLAPS_MAXIMUM = 3;
+    protected static final int LESS = 4;
+    protected static final int GREATER = 5;
+    
+    /** Add a new edge to the tree.
+     * @param edge is the edge to add.
+     */
+    public void add(final Edge edge) {
+      rootNode = addEdge(rootNode, edge, getMinimum(edge), getMaximum(edge));
+    }
+
+    /** Get the minimum value from the edge.
+     * @param edge is the edge.
+     * @return the minimum value.
+     */
+    protected abstract double getMinimum(final Edge edge);
+    
+    /** Get the maximum value from the edge.
+     * @param edge is the edge.
+     * @return the maximum value.
+     */
+    protected abstract double getMaximum(final Edge edge);
+    
+    /** Worker method for adding an edge.
+     * @param node is the node to add into.
+     * @param newEdge is the new edge to add.
+     * @param minimumValue is the minimum limit of the subrange of the edge we'll be adding.
+     * @param maximumValue is the maximum limit of the subrange of the edge we'll be adding.
+     * @return the updated node reference.
+     */
+    protected Node addEdge(final Node node, final Edge newEdge, final double minimumValue, final double maximumValue) {
+      if (node == null) {
+        // Create and return a new node
+        return new Node(newEdge, minimumValue, maximumValue);
+      }
+      // Compare with what's here
+      int result = compareForAdd(node.minimumValue, node.maximumValue, minimumValue, maximumValue);
+      switch (result) {
+      case CONTAINED:
+        // The node is contained in the range provided.  We need to create a new node and insert
+        // it into the "within" chain.
+        final Node rval = new Node(newEdge, minimumValue, maximumValue);
+        rval.within = node.within;
+        return rval;
+      case WITHIN:
+        // The new edge is within the node provided
+        node.within = addEdge(node.within, newEdge, minimumValue, maximumValue);
+        return node;
+      case OVERLAPS_MINIMUM:
+        // The new edge overlaps the minimum value, but not the maximum value.
+        // Here we need to create TWO entries: one for the lesser side, and one for the within chain.
+        final double lesserMaximum = Math.nextDown(node.minimumValue);
+        node.lesser = addEdge(node.lesser, newEdge, minimumValue, lesserMaximum);
+        return addEdge(node, newEdge, node.minimumValue, maximumValue);
+      case OVERLAPS_MAXIMUM:
+        // The new edge overlaps the maximum value, but not the minimum value.
+        // Need to create two entries, one on the greater side, and one back into the current node.
+        final double greaterMinimum = Math.nextUp(node.maximumValue);
+        node.greater = addEdge(node.greater, newEdge, greaterMinimum, maximumValue);
+        return addEdge(node, newEdge, minimumValue, node.maximumValue);
+      case LESS:
+        // The new edge is clearly less than the current node.
+        node.lesser = addEdge(node.lesser, newEdge, minimumValue, maximumValue);
+        return node;
+      case GREATER:
+        // The new edge is clearly greater than the current node.
+        node.greater = addEdge(node.greater, newEdge, minimumValue, maximumValue);
+        return node;
+      default:
+        throw new RuntimeException("Unexpected comparison result: "+result);
       }
+      
     }
     
-    public boolean traverse(final EdgeIterator edgeIterator, final TraverseComparator edgeComparator, final double minValue, final double maxValue) {
-      Node currentNode = this;
+    /** Traverse the tree, finding all edges that intersect the provided value.
+     * @param edgeIterator provides the method to call for any encountered matching edge.
+     * @param value is the value to match.
+     * @return false if the traversal was aborted before completion.
+     */
+    public boolean traverse(final EdgeIterator edgeIterator, final double value) {
+      // Since there is one distinct value we are looking for, we can just do a straight descent through the nodes.
+      Node currentNode = rootNode;
       while (currentNode != null) {
-        final int result = edgeComparator.compare(currentNode.edge, minValue, maxValue);
-        if (result < 0) {
+        if (value < currentNode.minimumValue) {
           currentNode = currentNode.lesser;
-        } else if (result > 0) {
+        } else if (value > currentNode.maximumValue) {
           currentNode = currentNode.greater;
         } else {
+          // We're within the bounds of the node.  Call the iterator, and descend
           if (!edgeIterator.matches(currentNode.edge)) {
             return false;
           }
-          currentNode = currentNode.overlaps;
+          currentNode = currentNode.within;
         }
       }
       return true;
     }
-  }
-  
-  /** An interface describing a tree.
-   */
-  private static interface Tree {
-    
-    public void add(final Edge edge);
     
-    public boolean traverse(final EdgeIterator edgeIterator, final double minValue, final double maxValue);
+    /** Traverse the tree, finding all edges that intersect the provided value range.
+     * @param edgeIterator provides the method to call for any encountered matching edge.
+     *   Edges will not be invoked more than once.
+     * @param minValue is the minimum value.
+     * @param maxValue is the maximum value.
+     * @return false if the traversal was aborted before completion.
+     */
+    public boolean traverse(final EdgeIterator edgeIterator, final double minValue, final double maxValue) {
+      // This is tricky because edges are duplicated in the tree (where they got split).
+      // We need to eliminate those duplicate edges as we traverse.  This requires us to keep a set of edges we've seen.
+      // Luckily, the number of edges we're likely to encounter in a real-world situation is small, so we can get away with it.
+      return traverseEdges(rootNode, edgeIterator, minValue, maxValue, new HashSet<>());
+    }
 
+    protected boolean traverseEdges(final Node node, final EdgeIterator edgeIterator, final double minValue, final double maxValue, final Set<Edge> edgeSet) {
+      if (node == null) {
+        return true;
+      }
+      if (maxValue < node.minimumValue) {
+        return traverseEdges(node.lesser, edgeIterator, minValue, maxValue, edgeSet);
+      } else if (minValue > node.maximumValue) {
+        return traverseEdges(node.greater, edgeIterator, minValue, maxValue, edgeSet);
+      } else {
+        // There's overlap with the current node, and there may also be overlap with the lesser side and greater side
+        if (minValue < node.minimumValue) {
+          if (!traverseEdges(node.lesser, edgeIterator, minValue, maxValue, edgeSet)) {
+            return false;
+          }
+        }
+        if (!edgeSet.contains(node.edge)) {
+          if (!edgeIterator.matches(node.edge)) {
+            return false;
+          }
+          edgeSet.add(node.edge);
+        }
+        if (maxValue > node.maximumValue) {
+          if (!traverseEdges(node.greater, edgeIterator, minValue, maxValue, edgeSet)) {
+            return false;
+          }
+        }
+        return traverseEdges(node.within, edgeIterator, minValue, maxValue, edgeSet);
+      }
+    }
+    
+    /** Compare a node against a subrange of a new edge.
+     * @param node is the node to compare.
+     * @param newEdge is the edge being added.
+     * @param minimumValue is the minimum value for the edge being added.
+     * @param maximumValue is the maximum value for the edge being added.
+     * @return the comparison result.
+     */
+    protected int compareForAdd(final double nodeMinimumValue, final double nodeMaximumValue, final double minimumValue, final double maximumValue) {
+      if (minimumValue <= nodeMinimumValue && maximumValue >= nodeMaximumValue) {
+        return CONTAINED;
+      } else if (nodeMinimumValue <= minimumValue && nodeMaximumValue >= maximumValue) {
+        return WITHIN;
+      } else if (maximumValue < nodeMinimumValue) {
+        return LESS;
+      } else if (minimumValue > nodeMaximumValue) {
+        return GREATER;
+      } else if (minimumValue < nodeMinimumValue) {
+        return OVERLAPS_MINIMUM;
+      } else {
+        return OVERLAPS_MAXIMUM;
+      }
+    }
   }
   
   /** This is the z-tree.
    */
-  private static class ZTree implements Tree, TraverseComparator, AddComparator {
+  private static class ZTree extends Tree {
     public Node rootNode = null;
     
     public ZTree() {
     }
     
     @Override
-    public void add(final Edge edge) {
-      if (rootNode == null) {
-        rootNode = new Node(edge);
-      } else {
-        rootNode.add(edge, this);
-      }
-    }
-    
-    @Override
-    public boolean traverse(final EdgeIterator edgeIterator, final double minValue, final double maxValue) {
-      if (rootNode == null) {
-        return true;
-      }
-      return rootNode.traverse(edgeIterator, this, minValue, maxValue);
+    protected double getMinimum(final Edge edge) {
+      return edge.planeBounds.getMinimumZ();
     }
     
     @Override
-    public int compare(final Edge edge, final Edge addEdge) {
-      if (edge.planeBounds.getMaximumZ() < addEdge.planeBounds.getMinimumZ()) {
-        return 1;
-      } else if (edge.planeBounds.getMinimumZ() > addEdge.planeBounds.getMaximumZ()) {
-        return -1;
-      }
-      return 0;
+    protected double getMaximum(final Edge edge) {
+      return edge.planeBounds.getMaximumZ();
     }
-    
-    @Override
-    public int compare(final Edge edge, final double minValue, final double maxValue) {
-      if (edge.planeBounds.getMinimumZ() > maxValue) {
-        return -1;
-      } else if (edge.planeBounds.getMaximumZ() < minValue) {
-        return 1;
-      }
-      return 0;
-    }
-    
+
   }
   
   /** This is the y-tree.
    */
-  private static class YTree implements Tree, TraverseComparator, AddComparator {
-    public Node rootNode = null;
+  private static class YTree extends Tree {
     
     public YTree() {
     }
     
     @Override
-    public void add(final Edge edge) {
-      if (rootNode == null) {
-        rootNode = new Node(edge);
-      } else {
-        rootNode.add(edge, this);
-      }
+    protected double getMinimum(final Edge edge) {
+      return edge.planeBounds.getMinimumY();
     }
     
     @Override
-    public boolean traverse(final EdgeIterator edgeIterator, final double minValue, final double maxValue) {
-      if (rootNode == null) {
-        return true;
-      }
-      return rootNode.traverse(edgeIterator, this, minValue, maxValue);
-    }
-    
-    @Override
-    public int compare(final Edge edge, final Edge addEdge) {
-      if (edge.planeBounds.getMaximumY() < addEdge.planeBounds.getMinimumY()) {
-        return 1;
-      } else if (edge.planeBounds.getMinimumY() > addEdge.planeBounds.getMaximumY()) {
-        return -1;
-      }
-      return 0;
-    }
-    
-    @Override
-    public int compare(final Edge edge, final double minValue, final double maxValue) {
-      if (edge.planeBounds.getMinimumY() > maxValue) {
-        return -1;
-      } else if (edge.planeBounds.getMaximumY() < minValue) {
-        return 1;
-      }
-      return 0;
+    protected double getMaximum(final Edge edge) {
+      return edge.planeBounds.getMaximumY();
     }
     
   }
 
   /** This is the x-tree.
    */
-  private static class XTree implements Tree, TraverseComparator, AddComparator {
-    public Node rootNode = null;
+  private static class XTree extends Tree {
     
     public XTree() {
     }
     
     @Override
-    public void add(final Edge edge) {
-      if (rootNode == null) {
-        rootNode = new Node(edge);
-      } else {
-        rootNode.add(edge, this);
-      }
+    protected double getMinimum(final Edge edge) {
+      return edge.planeBounds.getMinimumX();
     }
     
     @Override
-    public boolean traverse(final EdgeIterator edgeIterator, final double minValue, final double maxValue) {
-      if (rootNode == null) {
-        return true;
-      }
-      return rootNode.traverse(edgeIterator, this, minValue, maxValue);
-    }
-    
-    @Override
-    public int compare(final Edge edge, final Edge addEdge) {
-      if (edge.planeBounds.getMaximumX() < addEdge.planeBounds.getMinimumX()) {
-        return 1;
-      } else if (edge.planeBounds.getMinimumX() > addEdge.planeBounds.getMaximumX()) {
-        return -1;
-      }
-      return 0;
-    }
-    
-    @Override
-    public int compare(final Edge edge, final double minValue, final double maxValue) {
-      if (edge.planeBounds.getMinimumX() > maxValue) {
-        return -1;
-      } else if (edge.planeBounds.getMaximumX() < minValue) {
-        return 1;
-      }
-      return 0;
+    protected double getMaximum(final Edge edge) {
+      return edge.planeBounds.getMaximumX();
     }
     
   }
@@ -924,8 +935,10 @@ class GeoComplexPolygon extends GeoBasePolygon {
     
     @Override
     public boolean matches(final Edge edge) {
+      System.err.println("Processing edge "+edge);
       // Early exit if the point is on the edge.
       if (thePoint != null && edge.plane.evaluateIsZero(thePoint) && edge.startPlane.isWithin(thePoint) && edge.endPlane.isWithin(thePoint)) {
+        System.err.println(" Check point is on edge: isWithin = true");
         return false;
       }
       // If the intersection point lies on this edge, we should still be able to consider crossing points only.
@@ -942,11 +955,15 @@ class GeoComplexPolygon extends GeoBasePolygon {
         for (final GeoPoint crossingPoint : crossingPoints) {
           countCrossingPoint(crossingPoint, edge);
         }
+        System.err.println(" All crossing points processed");
+      } else {
+        System.err.println(" No crossing points!");
       }
       return true;
     }
 
     private void countCrossingPoint(final GeoPoint crossingPoint, final Edge edge) {
+      System.err.println(" Crossing point "+crossingPoint);
       // We consider crossing points only in this method.
       // Unlike the linear case, there are additional cases when:
       // (1) The crossing point and the intersection point are the same, but are not the endpoint of an edge;
@@ -960,12 +977,14 @@ class GeoComplexPolygon extends GeoBasePolygon {
       // In either case, we have to be sure to count each edge only once, since it might appear in both the
       // first leg and the second.  If the first leg can process it, it should, and the second should skip it.
       if (crossingPoint.isNumericallyIdentical(intersectionPoint)) {
+        System.err.println(" Crosses intersection point.");
         if (isSecondLeg) {
           // See whether this edge would have been processed in the first leg; if so, we skip it.
           final GeoPoint[] firstLegCrossings = testPointPlane.findCrossings(planetModel, edge.plane, testPointCutoffPlane, testPointOtherCutoffPlane, edge.startPlane, edge.endPlane);
           for (final GeoPoint firstLegCrossing : firstLegCrossings) {
             if (firstLegCrossing.isNumericallyIdentical(intersectionPoint)) {
               // We already processed it, so we're done here.
+              System.err.println("  Already processed on previous leg: exit");
               return;
             }
           }
@@ -994,6 +1013,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
       }
         
       if (crossingPoint.isNumericallyIdentical(edge.startPoint)) {
+        System.err.println(" Crossing point = edge.startPoint");
         // We have to figure out if this crossing should be counted.
           
         // Does the crossing for this edge go up, or down?  Or can't we tell?
@@ -1062,6 +1082,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
         }
           
       } else if (crossingPoint.isNumericallyIdentical(edge.endPoint)) {
+        System.err.println(" Crossing point = edge.endPoint");
         // Figure out if the crossing should be counted.
           
         // Does the crossing for this edge go up, or down?  Or can't we tell?
@@ -1115,6 +1136,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
           crossingCount++;
         }
       } else {
+        System.err.println(" Not a special case: incrementing crossing count");
         // Not a special case, so we can safely count a crossing.
         crossingCount++;
       }


[21/26] lucene-solr:master: Lots of fixes

Posted by kw...@apache.org.
Lots of fixes


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/72d536cb
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/72d536cb
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/72d536cb

Branch: refs/heads/master
Commit: 72d536cb11d8453c1ad60f3c8543dcb01d7c0a5f
Parents: d990d97
Author: Karl Wright <Da...@gmail.com>
Authored: Thu Apr 28 13:14:35 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Thu Apr 28 13:14:35 2016 -0400

----------------------------------------------------------------------
 .../spatial3d/geom/GeoComplexPolygon.java       | 92 ++++++++++++++------
 .../lucene/spatial3d/geom/GeoPolygonTest.java   |  6 +-
 2 files changed, 67 insertions(+), 31 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/72d536cb/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
index 3eff223..914b000 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
@@ -153,6 +153,8 @@ class GeoComplexPolygon extends GeoBasePolygon {
       return ((crossingEdgeIterator.crossingCount & 1) == 0)?testPointInSet:!testPointInSet;
     } else {
       
+      System.err.println("isWithin() for check point "+thePoint+", test point "+testPoint);
+      
       // We need to use two planes to get there.  We don't know which two planes will do it but we can figure it out.
       final Plane travelPlaneFixedX = new Plane(1.0, 0.0, 0.0, -thePoint.x);
       final Plane travelPlaneFixedY = new Plane(0.0, 1.0, 0.0, -thePoint.y);
@@ -185,12 +187,12 @@ class GeoComplexPolygon extends GeoBasePolygon {
           bestDistance = newDistance;
           firstLegValue = testPoint.y;
           secondLegValue = thePoint.x;
-          firstLegPlane = testPointYZPlane;
-          firstLegAbovePlane = testPointYZAbovePlane;
-          firstLegBelowPlane = testPointYZBelowPlane;
+          firstLegPlane = testPointXZPlane;
+          firstLegAbovePlane = testPointXZAbovePlane;
+          firstLegBelowPlane = testPointXZBelowPlane;
           secondLegPlane = travelPlaneFixedX;
-          firstLegTree = xTree;
-          secondLegTree = yTree;
+          firstLegTree = yTree;
+          secondLegTree = xTree;
           intersectionPoint = p;
         }
       }
@@ -201,10 +203,10 @@ class GeoComplexPolygon extends GeoBasePolygon {
           bestDistance = newDistance;
           firstLegValue = testPoint.y;
           secondLegValue = thePoint.z;
-          firstLegPlane = testPointXYPlane;
-          firstLegAbovePlane = testPointXYAbovePlane;
-          firstLegBelowPlane = testPointXYBelowPlane;
-          secondLegPlane = travelPlaneFixedX;
+          firstLegPlane = testPointXZPlane;
+          firstLegAbovePlane = testPointXZAbovePlane;
+          firstLegBelowPlane = testPointXZBelowPlane;
+          secondLegPlane = travelPlaneFixedZ;
           firstLegTree = yTree;
           secondLegTree = zTree;
           intersectionPoint = p;
@@ -217,9 +219,9 @@ class GeoComplexPolygon extends GeoBasePolygon {
           bestDistance = newDistance;
           firstLegValue = testPoint.x;
           secondLegValue = thePoint.y;
-          firstLegPlane = testPointXZPlane;
-          firstLegAbovePlane = testPointXZAbovePlane;
-          firstLegBelowPlane = testPointXZBelowPlane;
+          firstLegPlane = testPointYZPlane;
+          firstLegAbovePlane = testPointYZAbovePlane;
+          firstLegBelowPlane = testPointYZBelowPlane;
           secondLegPlane = travelPlaneFixedY;
           firstLegTree = xTree;
           secondLegTree = yTree;
@@ -233,10 +235,10 @@ class GeoComplexPolygon extends GeoBasePolygon {
           bestDistance = newDistance;
           firstLegValue = testPoint.x;
           secondLegValue = thePoint.z;
-          firstLegPlane = testPointXYPlane;
-          firstLegAbovePlane = testPointXYAbovePlane;
-          firstLegBelowPlane = testPointXYBelowPlane;
-          secondLegPlane = travelPlaneFixedX;
+          firstLegPlane = testPointYZPlane;
+          firstLegAbovePlane = testPointYZAbovePlane;
+          firstLegBelowPlane = testPointYZBelowPlane;
+          secondLegPlane = travelPlaneFixedZ;
           firstLegTree = xTree;
           secondLegTree = zTree;
           intersectionPoint = p;
@@ -249,10 +251,10 @@ class GeoComplexPolygon extends GeoBasePolygon {
           bestDistance = newDistance;
           firstLegValue = testPoint.z;
           secondLegValue = thePoint.x;
-          firstLegPlane = testPointYZPlane;
-          firstLegAbovePlane = testPointYZAbovePlane;
-          firstLegBelowPlane = testPointYZBelowPlane;
-          secondLegPlane = travelPlaneFixedZ;
+          firstLegPlane = testPointXYPlane;
+          firstLegAbovePlane = testPointXYAbovePlane;
+          firstLegBelowPlane = testPointXYBelowPlane;
+          secondLegPlane = travelPlaneFixedX;
           firstLegTree = zTree;
           secondLegTree = xTree;
           intersectionPoint = p;
@@ -265,10 +267,10 @@ class GeoComplexPolygon extends GeoBasePolygon {
           bestDistance = newDistance;
           firstLegValue = testPoint.z;
           secondLegValue = thePoint.y;
-          firstLegPlane = testPointXZPlane;
-          firstLegAbovePlane = testPointXZAbovePlane;
-          firstLegBelowPlane = testPointXZBelowPlane;
-          secondLegPlane = travelPlaneFixedZ;
+          firstLegPlane = testPointXYPlane;
+          firstLegAbovePlane = testPointXYAbovePlane;
+          firstLegBelowPlane = testPointXYBelowPlane;
+          secondLegPlane = travelPlaneFixedY;
           firstLegTree = zTree;
           secondLegTree = yTree;
           intersectionPoint = p;
@@ -388,7 +390,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
       this.planeBounds.addPoint(startPoint);
       this.planeBounds.addPoint(endPoint);
       this.plane.recordBounds(pm, this.planeBounds, this.startPlane, this.endPlane);
-      System.err.println("Recording edge from "+startPoint+" to "+endPoint+"; bounds = "+planeBounds);
+      System.err.println("Recording edge "+this+" from "+startPoint+" to "+endPoint+"; bounds = "+planeBounds);
     }
   }
   
@@ -473,8 +475,11 @@ class GeoComplexPolygon extends GeoBasePolygon {
     protected Node addEdge(final Node node, final Edge newEdge, final double minimumValue, final double maximumValue) {
       if (node == null) {
         // Create and return a new node
-        return new Node(newEdge, minimumValue, maximumValue);
+        final Node rval = new Node(newEdge, minimumValue, maximumValue);
+        //System.err.println("Creating new node "+rval+" for edge "+newEdge+" in tree "+this);
+        return rval;
       }
+      //System.err.println("Adding edge "+newEdge+" into node "+node+" in tree "+this);
       // Compare with what's here
       int result = compareForAdd(node.minimumValue, node.maximumValue, minimumValue, maximumValue);
       switch (result) {
@@ -482,30 +487,36 @@ class GeoComplexPolygon extends GeoBasePolygon {
         // The node is contained in the range provided.  We need to create a new node and insert
         // it into the "within" chain.
         final Node rval = new Node(newEdge, minimumValue, maximumValue);
-        rval.within = node.within;
+        //System.err.println(" Inserting new node "+rval+" at head of current 'within' chain in tree "+this);
+        rval.within = node;
         return rval;
       case WITHIN:
         // The new edge is within the node provided
+        //System.err.println(" Adding edge into 'within' chain in tree "+this);
         node.within = addEdge(node.within, newEdge, minimumValue, maximumValue);
         return node;
       case OVERLAPS_MINIMUM:
         // The new edge overlaps the minimum value, but not the maximum value.
         // Here we need to create TWO entries: one for the lesser side, and one for the within chain.
+        //System.err.println(" Inserting edge into BOTH lesser chain and within chain in tree "+this);
         final double lesserMaximum = Math.nextDown(node.minimumValue);
         node.lesser = addEdge(node.lesser, newEdge, minimumValue, lesserMaximum);
         return addEdge(node, newEdge, node.minimumValue, maximumValue);
       case OVERLAPS_MAXIMUM:
         // The new edge overlaps the maximum value, but not the minimum value.
         // Need to create two entries, one on the greater side, and one back into the current node.
+        //System.err.println(" Inserting edge into BOTH greater chain and within chain in tree "+this);
         final double greaterMinimum = Math.nextUp(node.maximumValue);
         node.greater = addEdge(node.greater, newEdge, greaterMinimum, maximumValue);
         return addEdge(node, newEdge, minimumValue, node.maximumValue);
       case LESS:
         // The new edge is clearly less than the current node.
+        //System.err.println(" Edge goes into the lesser chain in tree "+this);
         node.lesser = addEdge(node.lesser, newEdge, minimumValue, maximumValue);
         return node;
       case GREATER:
         // The new edge is clearly greater than the current node.
+        //System.err.println(" Edge goes into the greater chain in tree "+this);
         node.greater = addEdge(node.greater, newEdge, minimumValue, maximumValue);
         return node;
       default:
@@ -520,14 +531,18 @@ class GeoComplexPolygon extends GeoBasePolygon {
      * @return false if the traversal was aborted before completion.
      */
     public boolean traverse(final EdgeIterator edgeIterator, final double value) {
+      //System.err.println("Traversing tree, value = "+value);
       // Since there is one distinct value we are looking for, we can just do a straight descent through the nodes.
       Node currentNode = rootNode;
       while (currentNode != null) {
         if (value < currentNode.minimumValue) {
+          //System.err.println(" value is less than "+currentNode.minimumValue);
           currentNode = currentNode.lesser;
         } else if (value > currentNode.maximumValue) {
+          //System.err.println(" value is greater than "+currentNode.maximumValue);
           currentNode = currentNode.greater;
         } else {
+          //System.err.println(" value within "+currentNode.minimumValue+" to "+currentNode.maximumValue);
           // We're within the bounds of the node.  Call the iterator, and descend
           if (!edgeIterator.matches(currentNode.edge)) {
             return false;
@@ -535,6 +550,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
           currentNode = currentNode.within;
         }
       }
+      //System.err.println("Done with tree");
       return true;
     }
     
@@ -615,6 +631,12 @@ class GeoComplexPolygon extends GeoBasePolygon {
     }
     
     @Override
+    public boolean traverse(final EdgeIterator edgeIterator, final double value) {
+      System.err.println("Traversing in Z, value= "+value+"...");
+      return super.traverse(edgeIterator, value);
+    }
+
+    @Override
     protected double getMinimum(final Edge edge) {
       return edge.planeBounds.getMinimumZ();
     }
@@ -632,6 +654,12 @@ class GeoComplexPolygon extends GeoBasePolygon {
     
     public YTree() {
     }
+
+    @Override
+    public boolean traverse(final EdgeIterator edgeIterator, final double value) {
+      System.err.println("Traversing in Y, value= "+value+"...");
+      return super.traverse(edgeIterator, value);
+    }
     
     @Override
     protected double getMinimum(final Edge edge) {
@@ -653,6 +681,12 @@ class GeoComplexPolygon extends GeoBasePolygon {
     }
     
     @Override
+    public boolean traverse(final EdgeIterator edgeIterator, final double value) {
+      System.err.println("Traversing in X, value= "+value+"...");
+      return super.traverse(edgeIterator, value);
+    }
+    
+    @Override
     protected double getMinimum(final Edge edge) {
       return edge.planeBounds.getMinimumX();
     }
@@ -935,7 +969,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
     
     @Override
     public boolean matches(final Edge edge) {
-      System.err.println("Processing edge "+edge);
+      System.err.println("Processing edge "+edge+", startpoint="+edge.startPoint+" endpoint="+edge.endPoint);
       // Early exit if the point is on the edge.
       if (thePoint != null && edge.plane.evaluateIsZero(thePoint) && edge.startPlane.isWithin(thePoint) && edge.endPlane.isWithin(thePoint)) {
         System.err.println(" Check point is on edge: isWithin = true");
@@ -946,8 +980,10 @@ class GeoComplexPolygon extends GeoBasePolygon {
       // for at least one of the two planes in order to be a legitimate crossing of the combined path.
       final GeoPoint[] crossingPoints;
       if (isSecondLeg) {
+        System.err.println(" check point plane = "+travelPlane);
         crossingPoints = travelPlane.findCrossings(planetModel, edge.plane, checkPointCutoffPlane, checkPointOtherCutoffPlane, edge.startPlane, edge.endPlane);
       } else {
+        System.err.println(" test point plane = "+testPointPlane);
         crossingPoints = testPointPlane.findCrossings(planetModel, edge.plane, testPointCutoffPlane, testPointOtherCutoffPlane, edge.startPlane, edge.endPlane);
       }
       if (crossingPoints != null) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/72d536cb/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
index 7a152b9..53fc246 100755
--- a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
+++ b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
@@ -189,8 +189,8 @@ public class GeoPolygonTest {
     shapes.add(new GeoPolygonFactory.PolygonDescription(points));
     
     c = GeoPolygonFactory.makeLargeGeoPolygon(PlanetModel.SPHERE, shapes);
-    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.65);
-    assertFalse(c.isWithin(gp)); //??? fails
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.35);
+    assertFalse(c.isWithin(gp));
 
     // Sample some points within
     gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.45);
@@ -205,7 +205,7 @@ public class GeoPolygonTest {
     assertTrue(c.isWithin(gp));
     // Sample some nearby points outside
     gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.65);
-    assertFalse(c.isWithin(gp)); //??? fails
+    assertFalse(c.isWithin(gp));
     gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.35);
     assertFalse(c.isWithin(gp));
     gp = new GeoPoint(PlanetModel.SPHERE, -0.15, -0.5);


[04/26] lucene-solr:master: Flesh out the additional method needed in Plane, as well as intersection logic.

Posted by kw...@apache.org.
Flesh out the additional method needed in Plane, as well as intersection logic.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/d287ecae
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/d287ecae
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/d287ecae

Branch: refs/heads/master
Commit: d287ecaeedfa1852824c269885d4c02ec927c0da
Parents: b9c2bf7
Author: Karl Wright <Da...@gmail.com>
Authored: Mon Apr 25 02:40:31 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Mon Apr 25 02:40:31 2016 -0400

----------------------------------------------------------------------
 .../spatial3d/geom/GeoComplexPolygon.java       |  86 +++++++++---
 .../org/apache/lucene/spatial3d/geom/Plane.java | 139 ++++++++++++++++++-
 2 files changed, 202 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d287ecae/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
index df89f55..9ffbcc7 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
@@ -120,8 +120,27 @@ class GeoComplexPolygon extends GeoBasePolygon {
 
   @Override
   public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) {
-    // MHL
-    return false;
+    // Create the intersector
+    final EdgeIterator intersector = new IntersectorEdgeIterator(p, notablePoints, bounds);
+    // First, compute the bounds for the the plane
+    final XYZBounds xyzBounds = new XYZBounds();
+    p.recordBounds(xyzBounds);
+    // Figure out which tree likely works best
+    final double xDelta = xyzBounds.getMaximumX() - xyzBounds.getMinimumX();
+    final double yDelta = xyzBounds.getMaximumY() - xyzBounds.getMinimumY();
+    final double zDelta = xyzBounds.getMaximumZ() - xyzBounds.getMinimumZ();
+    // Select the smallest range
+    if (xDelta <= yDelta && xDelta <= zDelta) {
+      // Drill down in x
+      return !xtree.traverse(intersector, xyzBounds.getMinimumX(), xyzBounds.getMaximumX());
+    } else if (yDelta <= xDelta && yDelta <= zDelta) {
+      // Drill down in y
+      return !ytree.traverse(intersector, xyzBounds.getMinimumY(), xyzBounds.getMaximumY());
+    } else if (zDelta <= xDelta && zDelta <= yDelta) {
+      // Drill down in z
+      return !ztree.traverse(intersector, xyzBounds.getMinimumZ(), xyzBounds.getMaximumZ());
+    }
+    return true;
   }
 
 
@@ -153,6 +172,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
   private static class Edge {
     public final GeoPoint startPoint;
     public final GeoPoint endPoint;
+    public final GeoPoint[] notablePoints;
     public final SidedPlane startPlane;
     public final SidedPlane endPlane;
     public final Plane plane;
@@ -163,6 +183,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
     public Edge(final PlanetModel pm, final GeoPoint startPoint, final GeoPoint endPoint) {
       this.startPoint = startPoint;
       this.endPoint = endPoint;
+      this.notablePoints = new GeoPoint[]{startPoint, endPoint};
       this.plane = new Plane(startPoint, endPoint);
       this.startPlane =  new SidedPlane(endPoint, plane, startPoint);
       this.endPlane = new SidedPlane(startPoint, plane, endPoint);
@@ -193,10 +214,11 @@ class GeoComplexPolygon extends GeoBasePolygon {
     /**
      * Compare an edge.
      * @param edge is the edge to compare.
-     * @param value is the value to compare.
+     * @param minValue is the minimum value to compare (bottom of the range)
+     * @param maxValue is the maximum value to compare (top of the range)
      * @return -1 if "less" than this one, 0 if overlaps, or 1 if "greater".
      */
-    public int compare(final Edge edge, final double value);
+    public int compare(final Edge edge, final double minValue, final double maxValue);
     
   }
 
@@ -263,10 +285,10 @@ class GeoComplexPolygon extends GeoBasePolygon {
       }
     }
     
-    public boolean traverse(final EdgeIterator edgeIterator, final TraverseComparator edgeComparator, final double value) {
+    public boolean traverse(final EdgeIterator edgeIterator, final TraverseComparator edgeComparator, final double minValue, final double maxValue) {
       Node currentNode = this;
       while (currentNode != null) {
-        final int result = edgeComparator.compare(currentNode.edge, value);
+        final int result = edgeComparator.compare(currentNode.edge, minValue, maxValue);
         if (result < 0) {
           currentNode = lesser;
         } else if (result > 0) {
@@ -298,11 +320,11 @@ class GeoComplexPolygon extends GeoBasePolygon {
       }
     }
     
-    public boolean traverse(final EdgeIterator edgeIterator, final double value) {
+    public boolean traverse(final EdgeIterator edgeIterator, final double minValue, final double maxValue) {
       if (rootNode == null) {
         return true;
       }
-      return rootNode.traverse(edgeIterator, this, value);
+      return rootNode.traverse(edgeIterator, this, minValue, maxValue);
     }
     
     @Override
@@ -316,10 +338,10 @@ class GeoComplexPolygon extends GeoBasePolygon {
     }
     
     @Override
-    public int compare(final Edge edge, final double value) {
-      if (edge.planeBounds.getMinimumZ() > value) {
+    public int compare(final Edge edge, final double minValue, final double maxValue) {
+      if (edge.planeBounds.getMinimumZ() > maxValue) {
         return -1;
-      } else if (edge.planeBounds.getMaximumZ() < value) {
+      } else if (edge.planeBounds.getMaximumZ() < minValue) {
         return 1;
       }
       return 0;
@@ -343,11 +365,11 @@ class GeoComplexPolygon extends GeoBasePolygon {
       }
     }
     
-    public boolean traverse(final EdgeIterator edgeIterator, final double value) {
+    public boolean traverse(final EdgeIterator edgeIterator, final double minValue, final double maxValue) {
       if (rootNode == null) {
         return true;
       }
-      return rootNode.traverse(edgeIterator, this, value);
+      return rootNode.traverse(edgeIterator, this, minValue, maxValue);
     }
     
     @Override
@@ -361,10 +383,10 @@ class GeoComplexPolygon extends GeoBasePolygon {
     }
     
     @Override
-    public int compare(final Edge edge, final double value) {
-      if (edge.planeBounds.getMinimumY() > value) {
+    public int compare(final Edge edge, final double minValue, final double maxValue) {
+      if (edge.planeBounds.getMinimumY() > maxValue) {
         return -1;
-      } else if (edge.planeBounds.getMaximumY() < value) {
+      } else if (edge.planeBounds.getMaximumY() < minValue) {
         return 1;
       }
       return 0;
@@ -388,11 +410,11 @@ class GeoComplexPolygon extends GeoBasePolygon {
       }
     }
     
-    public boolean traverse(final EdgeIterator edgeIterator, final double value) {
+    public boolean traverse(final EdgeIterator edgeIterator, final double minValue, final double maxValue) {
       if (rootNode == null) {
         return true;
       }
-      return rootNode.traverse(edgeIterator, this, value);
+      return rootNode.traverse(edgeIterator, this, minValue, maxValue);
     }
     
     @Override
@@ -406,10 +428,10 @@ class GeoComplexPolygon extends GeoBasePolygon {
     }
     
     @Override
-    public int compare(final Edge edge, final double value) {
-      if (edge.planeBounds.getMinimumX() > value) {
+    public int compare(final Edge edge, final double minValue, final double maxValue) {
+      if (edge.planeBounds.getMinimumX() > maxValue) {
         return -1;
-      } else if (edge.planeBounds.getMaximumX() < value) {
+      } else if (edge.planeBounds.getMaximumX() < minValue) {
         return 1;
       }
       return 0;
@@ -417,6 +439,27 @@ class GeoComplexPolygon extends GeoBasePolygon {
     
   }
 
+  /** Assess whether edge intersects the provided plane plus bounds.
+   */
+  private class IntersectorEdgeIterator implements EdgeIterator {
+    
+    private final Plane plane;
+    private final GeoPoint[] notablePoints;
+    private final Membership[] bounds;
+    
+    public IntersectorEdgeIterator(final Plane plane, final GeoPoint[] notablePoints, final Membership... bounds) {
+      this.plane = plane;
+      this notablePoints = notablePoints;
+      this.bounds = bounds;
+    }
+    
+    @Override
+    public boolean matches(final Edge edge) {
+      return !plane.intersects(planetModel, edge.plane, notablePoints, edge.notablePoints, bounds, edge.startPlane, edge.endPlane);
+    }
+
+  }
+  
   @Override
   public boolean equals(Object o) {
     // MHL
@@ -426,6 +469,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
   @Override
   public int hashCode() {
     // MHL
+    return 0;
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d287ecae/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Plane.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Plane.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Plane.java
index f0df49d..a205ca9 100755
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Plane.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Plane.java
@@ -614,11 +614,11 @@ public class Plane extends Vector {
   }
 
   /**
-   * Public version of findIntersections.
+   * Find the intersection points between two planes, given a set of bounds.
    * @param planetModel is the planet model.
    * @param q is the plane to intersect with.
    * @param bounds are the bounds to consider to determine legal intersection points.
-   * @return the set of legal intersection points.
+   * @return the set of legal intersection points, or null if the planes are numerically identical.
    */
   public GeoPoint[] findIntersections(final PlanetModel planetModel, final Plane q, final Membership... bounds) {
     if (isNumericallyIdentical(q)) {
@@ -626,6 +626,23 @@ public class Plane extends Vector {
     }
     return findIntersections(planetModel, q, bounds, NO_BOUNDS);
   }
+
+  /**
+   * Find the points between two planes, where one plane crosses the other, given a set of bounds.
+   * Crossing is not just intersection; the planes cannot touch at just one point on the ellipsoid,
+   * but must cross at two.
+   *
+   * @param planetModel is the planet model.
+   * @param q is the plane to intersect with.
+   * @param bounds are the bounds to consider to determine legal intersection points.
+   * @return the set of legal crossing points, or null if the planes are numerically identical.
+   */
+  public GeoPoint[] findCrossings(final PlanetModel planetModel, final Plane q, final Membership... bounds) {
+    if (isNumericallyIdentical(q)) {
+      return null;
+    }
+    return findCrossings(planetModel, q, bounds, NO_BOUNDS);
+  }
   
   /**
    * Find the intersection points between two planes, given a set of bounds.
@@ -755,6 +772,124 @@ public class Plane extends Vector {
     }
   }
 
+  /**
+   * Find the points between two planes, where one plane crosses the other, given a set of bounds.
+   * Crossing is not just intersection; the planes cannot touch at just one point on the ellipsoid,
+   * but must cross at two.
+   *
+   * @param planetModel is the planet model to use in finding points.
+   * @param q          is the plane to intersect with.
+   * @param bounds     is the set of bounds.
+   * @param moreBounds is another set of bounds.
+   * @return the intersection point(s) on the ellipsoid, if there are any.
+   */
+  protected GeoPoint[] findCrosses(final PlanetModel planetModel, final Plane q, final Membership[] bounds, final Membership[] moreBounds) {
+    // This code in this method is very similar to findIntersections(), but eliminates the cases where
+    // crossings are detected.
+    // Unnormalized, unchecked...
+    final Vector lineVector = new Vector(y * q.z - z * q.y, z * q.x - x * q.z, x * q.y - y * q.x);
+    if (Math.abs(lineVector.x) < MINIMUM_RESOLUTION && Math.abs(lineVector.y) < MINIMUM_RESOLUTION && Math.abs(lineVector.z) < MINIMUM_RESOLUTION) {
+      // Degenerate case: parallel planes
+      return NO_POINTS;
+    }
+
+    // The line will have the equation: A t + A0 = x, B t + B0 = y, C t + C0 = z.
+    // We have A, B, and C.  In order to come up with A0, B0, and C0, we need to find a point that is on both planes.
+    // To do this, we find the largest vector value (either x, y, or z), and look for a point that solves both plane equations
+    // simultaneous.  For example, let's say that the vector is (0.5,0.5,1), and the two plane equations are:
+    // 0.7 x + 0.3 y + 0.1 z + 0.0 = 0
+    // and
+    // 0.9 x - 0.1 y + 0.2 z + 4.0 = 0
+    // Then we'd pick z = 0, so the equations to solve for x and y would be:
+    // 0.7 x + 0.3y = 0.0
+    // 0.9 x - 0.1y = -4.0
+    // ... which can readily be solved using standard linear algebra.  Generally:
+    // Q0 x + R0 y = S0
+    // Q1 x + R1 y = S1
+    // ... can be solved by Cramer's rule:
+    // x = det(S0 R0 / S1 R1) / det(Q0 R0 / Q1 R1)
+    // y = det(Q0 S0 / Q1 S1) / det(Q0 R0 / Q1 R1)
+    // ... where det( a b / c d ) = ad - bc, so:
+    // x = (S0 * R1 - R0 * S1) / (Q0 * R1 - R0 * Q1)
+    // y = (Q0 * S1 - S0 * Q1) / (Q0 * R1 - R0 * Q1)
+    double x0;
+    double y0;
+    double z0;
+    // We try to maximize the determinant in the denominator
+    final double denomYZ = this.y * q.z - this.z * q.y;
+    final double denomXZ = this.x * q.z - this.z * q.x;
+    final double denomXY = this.x * q.y - this.y * q.x;
+    if (Math.abs(denomYZ) >= Math.abs(denomXZ) && Math.abs(denomYZ) >= Math.abs(denomXY)) {
+      // X is the biggest, so our point will have x0 = 0.0
+      if (Math.abs(denomYZ) < MINIMUM_RESOLUTION_SQUARED) {
+        return NO_POINTS;
+      }
+      final double denom = 1.0 / denomYZ;
+      x0 = 0.0;
+      y0 = (-this.D * q.z - this.z * -q.D) * denom;
+      z0 = (this.y * -q.D + this.D * q.y) * denom;
+    } else if (Math.abs(denomXZ) >= Math.abs(denomXY) && Math.abs(denomXZ) >= Math.abs(denomYZ)) {
+      // Y is the biggest, so y0 = 0.0
+      if (Math.abs(denomXZ) < MINIMUM_RESOLUTION_SQUARED) {
+        return NO_POINTS;
+      }
+      final double denom = 1.0 / denomXZ;
+      x0 = (-this.D * q.z - this.z * -q.D) * denom;
+      y0 = 0.0;
+      z0 = (this.x * -q.D + this.D * q.x) * denom;
+    } else {
+      // Z is the biggest, so Z0 = 0.0
+      if (Math.abs(denomXY) < MINIMUM_RESOLUTION_SQUARED) {
+        return NO_POINTS;
+      }
+      final double denom = 1.0 / denomXY;
+      x0 = (-this.D * q.y - this.y * -q.D) * denom;
+      y0 = (this.x * -q.D + this.D * q.x) * denom;
+      z0 = 0.0;
+    }
+
+    // Once an intersecting line is determined, the next step is to intersect that line with the ellipsoid, which
+    // will yield zero, one, or two points.
+    // The ellipsoid equation: 1,0 = x^2/a^2 + y^2/b^2 + z^2/c^2
+    // 1.0 = (At+A0)^2/a^2 + (Bt+B0)^2/b^2 + (Ct+C0)^2/c^2
+    // A^2 t^2 / a^2 + 2AA0t / a^2 + A0^2 / a^2 + B^2 t^2 / b^2 + 2BB0t / b^2 + B0^2 / b^2 + C^2 t^2 / c^2 + 2CC0t / c^2 + C0^2 / c^2  - 1,0 = 0.0
+    // [A^2 / a^2 + B^2 / b^2 + C^2 / c^2] t^2 + [2AA0 / a^2 + 2BB0 / b^2 + 2CC0 / c^2] t + [A0^2 / a^2 + B0^2 / b^2 + C0^2 / c^2 - 1,0] = 0.0
+    // Use the quadratic formula to determine t values and candidate point(s)
+    final double A = lineVector.x * lineVector.x * planetModel.inverseAbSquared +
+      lineVector.y * lineVector.y * planetModel.inverseAbSquared +
+      lineVector.z * lineVector.z * planetModel.inverseCSquared;
+    final double B = 2.0 * (lineVector.x * x0 * planetModel.inverseAbSquared + lineVector.y * y0 * planetModel.inverseAbSquared + lineVector.z * z0 * planetModel.inverseCSquared);
+    final double C = x0 * x0 * planetModel.inverseAbSquared + y0 * y0 * planetModel.inverseAbSquared + z0 * z0 * planetModel.inverseCSquared - 1.0;
+
+    final double BsquaredMinus = B * B - 4.0 * A * C;
+    if (Math.abs(BsquaredMinus) < MINIMUM_RESOLUTION_SQUARED) {
+      // One point of intersection: cannot be a crossing.
+      return NO_POINTS;
+    } else if (BsquaredMinus > 0.0) {
+      final double inverse2A = 1.0 / (2.0 * A);
+      // Two solutions
+      final double sqrtTerm = Math.sqrt(BsquaredMinus);
+      final double t1 = (-B + sqrtTerm) * inverse2A;
+      final double t2 = (-B - sqrtTerm) * inverse2A;
+      GeoPoint point1 = new GeoPoint(lineVector.x * t1 + x0, lineVector.y * t1 + y0, lineVector.z * t1 + z0);
+      GeoPoint point2 = new GeoPoint(lineVector.x * t2 + x0, lineVector.y * t2 + y0, lineVector.z * t2 + z0);
+      //verifyPoint(planetModel, point1, q);
+      //verifyPoint(planetModel, point2, q);
+      //System.err.println("  "+point1+" and "+point2);
+      if (point1.isWithin(bounds, moreBounds)) {
+        if (point2.isWithin(bounds, moreBounds))
+          return new GeoPoint[]{point1, point2};
+        return new GeoPoint[]{point1};
+      }
+      if (point2.isWithin(bounds, moreBounds))
+        return new GeoPoint[]{point2};
+      return NO_POINTS;
+    } else {
+      // No solutions.
+      return NO_POINTS;
+    }
+  }
+
   /*
   protected void verifyPoint(final PlanetModel planetModel, final GeoPoint point, final Plane q) {
     if (!evaluateIsZero(point))


[24/26] lucene-solr:master: Add more tests, and catch degenerate case early

Posted by kw...@apache.org.
Add more tests, and catch degenerate case early


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/31176d1d
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/31176d1d
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/31176d1d

Branch: refs/heads/master
Commit: 31176d1de2160acaa51def38dee076626f381527
Parents: da896f8
Author: Karl Wright <Da...@gmail.com>
Authored: Thu Apr 28 19:27:36 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Thu Apr 28 19:27:36 2016 -0400

----------------------------------------------------------------------
 .../spatial3d/geom/GeoPolygonFactory.java       |  6 +++-
 .../lucene/spatial3d/geom/GeoPolygonTest.java   | 33 ++++++++++++++++++++
 2 files changed, 38 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/31176d1d/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java
index 81aaa65..cb4fcf4 100755
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java
@@ -88,7 +88,11 @@ public class GeoPolygonFactory {
     // First, exercise a sanity filter on the provided pointList, and remove identical points, linear points, and backtracks
     //System.err.println(" filtering "+pointList.size()+" points...");
     //final long startTime = System.currentTimeMillis();
-    final List<GeoPoint> filteredPointList = filterEdges(filterPoints(pointList), leniencyValue);
+    final List<GeoPoint> firstFilteredPointList = filterPoints(pointList);
+    if (firstFilteredPointList == null) {
+      return null;
+    }
+    final List<GeoPoint> filteredPointList = filterEdges(firstFilteredPointList, leniencyValue);
     //System.err.println("  ...done in "+(System.currentTimeMillis()-startTime)+"ms ("+((filteredPointList==null)?"degenerate":(filteredPointList.size()+" points"))+")");
     if (filteredPointList == null) {
       return null;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/31176d1d/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
index b325e43..8063cb0 100755
--- a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
+++ b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
@@ -264,6 +264,39 @@ public class GeoPolygonTest {
     gp = new GeoPoint(PlanetModel.SPHERE, 0.0, Math.PI);
     assertFalse(c.isWithin(gp));
 
+    // Now, same thing for large polygon
+    shapes = new ArrayList<>();
+    shapes.add(new GeoPolygonFactory.PolygonDescription(points));
+    
+    c = GeoPolygonFactory.makeLargeGeoPolygon(PlanetModel.SPHERE, shapes);
+    // Sample some points within
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.5);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.55);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.45);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.05, -0.5);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.05, -0.5);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.7);
+    assertTrue(c.isWithin(gp));
+    // Sample some nearby points outside
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.35);
+    assertFalse(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.15, -0.5);
+    assertFalse(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.15, -0.5);
+    assertFalse(c.isWithin(gp));
+    // Random points outside
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0);
+    assertFalse(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, Math.PI * 0.5, 0.0);
+    assertFalse(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, Math.PI);
+    assertFalse(c.isWithin(gp));
+
   }
 
   @Test


[10/26] lucene-solr:master: Have two separate crossing counters; one for dual leg, one for single.

Posted by kw...@apache.org.
Have two separate crossing counters; one for dual leg, one for single.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/42d3f3d1
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/42d3f3d1
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/42d3f3d1

Branch: refs/heads/master
Commit: 42d3f3d1c1d897938557de0f2d6e44f564cc45c1
Parents: 4105fda
Author: Karl Wright <Da...@gmail.com>
Authored: Tue Apr 26 02:46:03 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Tue Apr 26 02:46:03 2016 -0400

----------------------------------------------------------------------
 .../spatial3d/geom/GeoComplexPolygon.java       | 306 ++++++++++++++-----
 1 file changed, 234 insertions(+), 72 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/42d3f3d1/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
index a3ef6ab..88db3d6 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
@@ -133,9 +133,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
     // If we're right on top of any of the test planes, we navigate solely on that plane.
     if (testPointXZPlane.evaluateIsZero(thePoint)) {
       // Use the XZ plane exclusively.
-      final SidedPlane testPointCutoff =  new SidedPlane(thePoint, testPointXZPlane, testPoint);
-      final SidedPlane checkPointCutoff = new SidedPlane(testPoint, testPointXZPlane, thePoint);
-      final CrossingEdgeIterator crossingEdgeIterator = new CrossingEdgeIterator(testPointXZPlane, testPointXZAbovePlane, testPointXZBelowPlane, testPointCutoff, checkPointCutoff, thePoint);
+      final LinearCrossingEdgeIterator crossingEdgeIterator = new LinearCrossingEdgeIterator(testPointXZPlane, testPointXZAbovePlane, testPointXZBelowPlane, testPoint, thePoint);
       // Traverse our way from the test point to the check point.  Use the y tree because that's fixed.
       if (!yTree.traverse(crossingEdgeIterator, testPoint.y, testPoint.y)) {
         // Endpoint is on edge
@@ -144,9 +142,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
       return ((crossingEdgeIterator.crossingCount & 1) == 0)?testPointInSet:!testPointInSet;
     } else if (testPointYZPlane.evaluateIsZero(thePoint)) {
       // Use the YZ plane exclusively.
-      final SidedPlane testPointCutoff =  new SidedPlane(thePoint, testPointYZPlane, testPoint);
-      final SidedPlane checkPointCutoff = new SidedPlane(testPoint, testPointYZPlane, thePoint);
-      final CrossingEdgeIterator crossingEdgeIterator = new CrossingEdgeIterator(testPointYZPlane, testPointYZAbovePlane, testPointYZBelowPlane, testPointCutoff, checkPointCutoff, thePoint);
+      final LinearCrossingEdgeIterator crossingEdgeIterator = new LinearCrossingEdgeIterator(testPointYZPlane, testPointYZAbovePlane, testPointYZBelowPlane, testPoint, thePoint);
       // Traverse our way from the test point to the check point.  Use the x tree because that's fixed.
       if (!xTree.traverse(crossingEdgeIterator, testPoint.x, testPoint.x)) {
         // Endpoint is on edge
@@ -155,9 +151,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
       return ((crossingEdgeIterator.crossingCount & 1) == 0)?testPointInSet:!testPointInSet;
     } else if (testPointXYPlane.evaluateIsZero(thePoint)) {
       // Use the XY plane exclusively.
-      final SidedPlane testPointCutoff =  new SidedPlane(thePoint, testPointXYPlane, testPoint);
-      final SidedPlane checkPointCutoff = new SidedPlane(testPoint, testPointXYPlane, thePoint);
-      final CrossingEdgeIterator crossingEdgeIterator = new CrossingEdgeIterator(testPointXYPlane, testPointXYAbovePlane, testPointXYBelowPlane, testPointCutoff, checkPointCutoff, thePoint);
+      final LinearCrossingEdgeIterator crossingEdgeIterator = new LinearCrossingEdgeIterator(testPointXYPlane, testPointXYAbovePlane, testPointXYBelowPlane, testPoint, thePoint);
       // Traverse our way from the test point to the check point.  Use the z tree because that's fixed.
       if (!zTree.traverse(crossingEdgeIterator, testPoint.z, testPoint.z)) {
         // Endpoint is on edge
@@ -172,77 +166,41 @@ class GeoComplexPolygon extends GeoBasePolygon {
         // Travel in X and Y
         // We'll do this using the testPointYZPlane, and create a travel plane for the right XZ plane.
         final Plane travelPlane = new Plane(0.0, 1.0, 0.0, -thePoint.y);
-        final Plane travelAbovePlane = new Plane(travelPlane, true);
-        final Plane travelBelowPlane = new Plane(travelPlane, false);
-        // We need cutoff planes for both legs.
-        final SidedPlane testPointCutoffPlane = new SidedPlane(thePoint, testPointYZPlane, testPoint);
-        final SidedPlane checkPointCutoffPlane = new SidedPlane(testPoint, travelPlane, thePoint);
-        // Now, find the intersection of the check and test point planes.
-        final GeoPoint[] intersectionPoints = travelPlane.findIntersections(planetModel, testPointYZPlane, testPointCutoffPlane, checkPointCutoffPlane);
-        assert intersectionPoints != null : "couldn't find any intersections";
-        assert intersectionPoints.length != 1 : "wrong number of intersection points";
-        final SidedPlane testPointOtherCutoffPlane = new SidedPlane(testPoint, testPointYZPlane, intersectionPoints[0]);
-        final SidedPlane checkPointOtherCutoffPlane = new SidedPlane(thePoint, travelPlane, intersectionPoints[0]);
-        // Note: we need to handle the cases where end point of the leg sits on an edge!
-        // MHL
-        final CrossingEdgeIterator testPointEdgeIterator = new CrossingEdgeIterator(testPointYZPlane, testPointYZAbovePlane, testPointYZBelowPlane, testPointCutoffPlane, testPointOtherCutoffPlane, null);
-        xTree.traverse(testPointEdgeIterator, testPoint.x, testPoint.x);
-        final CrossingEdgeIterator checkPointEdgeIterator = new CrossingEdgeIterator(travelPlane, travelAbovePlane, travelBelowPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane, thePoint);
-        if (!yTree.traverse(checkPointEdgeIterator, thePoint.y, thePoint.y)) {
-          // Endpoint is on edge
+        final DualCrossingEdgeIterator edgeIterator = new DualCrossingEdgeIterator(testPointYZPlane, testPointYZAbovePlane, testPointYZBelowPlane, travelPlane, testPoint, thePoint);
+        if (!xTree.traverse(edgeIterator, testPoint.x, testPoint.x)) {
           return true;
         }
-        return (((testPointEdgeIterator.crossingCount + checkPointEdgeIterator.crossingCount) & 1) == 0)?testPointInSet:!testPointInSet;
+        edgeIterator.setSecondLeg();
+        if (!yTree.traverse(edgeIterator, thePoint.y, thePoint.y)) {
+          return true;
+        }
+        return ((edgeIterator.crossingCount  & 1) == 0)?testPointInSet:!testPointInSet;
       } else if (xDelta + zDelta <= xDelta + yDelta && xDelta + zDelta <= zDelta + yDelta) {
         // Travel in X and Z
         // We'll do this using the testPointXYPlane, and create a travel plane for the right YZ plane.
         final Plane travelPlane = new Plane(1.0, 0.0, 0.0, -thePoint.x);
-        final Plane travelAbovePlane = new Plane(travelPlane, true);
-        final Plane travelBelowPlane = new Plane(travelPlane, false);
-        // We need cutoff planes for both legs.
-        final SidedPlane testPointCutoffPlane = new SidedPlane(thePoint, testPointXYPlane, testPoint);
-        final SidedPlane checkPointCutoffPlane = new SidedPlane(testPoint, travelPlane, thePoint);
-        // Now, find the intersection of the check and test point planes.
-        final GeoPoint[] intersectionPoints = travelPlane.findIntersections(planetModel, testPointXYPlane, testPointCutoffPlane, checkPointCutoffPlane);
-        assert intersectionPoints != null : "couldn't find any intersections";
-        assert intersectionPoints.length != 1 : "wrong number of intersection points";
-        final SidedPlane testPointOtherCutoffPlane = new SidedPlane(testPoint, testPointXYPlane, intersectionPoints[0]);
-        final SidedPlane checkPointOtherCutoffPlane = new SidedPlane(thePoint, travelPlane, intersectionPoints[0]);
-        // Note: we need to handle the cases where end point of the leg sits on an edge!
-        // MHL
-        final CrossingEdgeIterator testPointEdgeIterator = new CrossingEdgeIterator(testPointXYPlane, testPointXYAbovePlane, testPointXYBelowPlane, testPointCutoffPlane, testPointOtherCutoffPlane, null);
-        zTree.traverse(testPointEdgeIterator, testPoint.z, testPoint.z);
-        final CrossingEdgeIterator checkPointEdgeIterator = new CrossingEdgeIterator(travelPlane, travelAbovePlane, travelBelowPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane, thePoint);
-        if (!xTree.traverse(checkPointEdgeIterator, thePoint.x, thePoint.x)) {
-          // Endpoint is on edge
+        final DualCrossingEdgeIterator edgeIterator = new DualCrossingEdgeIterator(testPointXYPlane, testPointXYAbovePlane, testPointXYBelowPlane, travelPlane, testPoint, thePoint);
+        if (!zTree.traverse(edgeIterator, testPoint.z, testPoint.z)) {
           return true;
         }
-        return (((testPointEdgeIterator.crossingCount + checkPointEdgeIterator.crossingCount) & 1) == 0)?testPointInSet:!testPointInSet;
+        edgeIterator.setSecondLeg();
+        if (!xTree.traverse(edgeIterator, thePoint.x, thePoint.x)) {
+          return true;
+        }
+        return ((edgeIterator.crossingCount & 1) == 0)?testPointInSet:!testPointInSet;
       } else if (yDelta + zDelta <= xDelta + yDelta && yDelta + zDelta <= xDelta + zDelta) {
         // Travel in Y and Z
         // We'll do this using the testPointXZPlane, and create a travel plane for the right XY plane.
         final Plane travelPlane = new Plane(0.0, 0.0, 1.0, -thePoint.z);
-        final Plane travelAbovePlane = new Plane(travelPlane, true);
-        final Plane travelBelowPlane = new Plane(travelPlane, false);
-        // We need cutoff planes for both legs.
-        final SidedPlane testPointCutoffPlane = new SidedPlane(thePoint, testPointXZPlane, testPoint);
-        final SidedPlane checkPointCutoffPlane = new SidedPlane(testPoint, travelPlane, thePoint);
-        // Now, find the intersection of the check and test point planes.
-        final GeoPoint[] intersectionPoints = travelPlane.findIntersections(planetModel, testPointXZPlane, testPointCutoffPlane, checkPointCutoffPlane);
-        assert intersectionPoints != null : "couldn't find any intersections";
-        assert intersectionPoints.length != 1 : "wrong number of intersection points";
-        final SidedPlane testPointOtherCutoffPlane = new SidedPlane(testPoint, testPointXZPlane, intersectionPoints[0]);
-        final SidedPlane checkPointOtherCutoffPlane = new SidedPlane(thePoint, travelPlane, intersectionPoints[0]);
-        // Note: we need to handle the cases where end point of the first leg sits on an edge!
-        // MHL
-        final CrossingEdgeIterator testPointEdgeIterator = new CrossingEdgeIterator(testPointXZPlane, testPointXZAbovePlane, testPointXZBelowPlane, testPointCutoffPlane, testPointOtherCutoffPlane, null);
-        yTree.traverse(testPointEdgeIterator, testPoint.y, testPoint.y);
-        final CrossingEdgeIterator checkPointEdgeIterator = new CrossingEdgeIterator(travelPlane, travelAbovePlane, travelBelowPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane, thePoint);
-        if (!zTree.traverse(checkPointEdgeIterator, thePoint.z, thePoint.z)) {
-          // Endpoint is on edge
+        final DualCrossingEdgeIterator edgeIterator = new DualCrossingEdgeIterator(testPointXZPlane, testPointXZAbovePlane, testPointXZBelowPlane, travelPlane, testPoint, thePoint);
+        if (!yTree.traverse(edgeIterator, testPoint.y, testPoint.y)) {
+          return true;
+        }
+        edgeIterator.setSecondLeg();
+        if (!zTree.traverse(edgeIterator, thePoint.z, thePoint.z)) {
           return true;
         }
-        return (((testPointEdgeIterator.crossingCount + checkPointEdgeIterator.crossingCount) & 1) == 0)?testPointInSet:!testPointInSet;
+        return ((edgeIterator.crossingCount & 1) == 0)?testPointInSet:!testPointInSet;
       }
     }
     return false;
@@ -597,7 +555,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
   
   /** Count the number of verifiable edge crossings.
    */
-  private class CrossingEdgeIterator implements EdgeIterator {
+  private class LinearCrossingEdgeIterator implements EdgeIterator {
     
     private final Plane plane;
     private final Plane abovePlane;
@@ -608,12 +566,12 @@ class GeoComplexPolygon extends GeoBasePolygon {
     
     public int crossingCount = 0;
     
-    public CrossingEdgeIterator(final Plane plane, final Plane abovePlane, final Plane belowPlane, final Membership bound1, final Membership bound2, final Vector thePoint) {
+    public LinearCrossingEdgeIterator(final Plane plane, final Plane abovePlane, final Plane belowPlane, final Vector testPoint, final Vector thePoint) {
       this.plane = plane;
       this.abovePlane = abovePlane;
       this.belowPlane = belowPlane;
-      this.bound1 = bound1;
-      this.bound2 = bound2;
+      this.bound1 = new SidedPlane(thePoint, plane, testPoint);
+      this.bound2 = new SidedPlane(testPoint, plane, thePoint);
       this.thePoint = thePoint;
     }
     
@@ -627,13 +585,217 @@ class GeoComplexPolygon extends GeoBasePolygon {
       if (crossingPoints != null) {
         // We need to handle the endpoint case, which is quite tricky.
         for (final GeoPoint crossingPoint : crossingPoints) {
-          countCrossingPoint(crossingPoint, plane, edge);
+          countCrossingPoint(crossingPoint, edge);
+        }
+      }
+      return true;
+    }
+
+    private void countCrossingPoint(final GeoPoint crossingPoint, final Edge edge) {
+      if (crossingPoint.isNumericallyIdentical(edge.startPoint)) {
+        // We have to figure out if this crossing should be counted.
+        
+        // Does the crossing for this edge go up, or down?  Or can't we tell?
+        final GeoPoint[] aboveIntersections = abovePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
+        final GeoPoint[] belowIntersections = belowPlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
+        
+        assert !(aboveIntersections.length > 0 && belowIntersections.length > 0) : "edge that ends in a crossing can't both up and down";
+        
+        if (aboveIntersections.length == 0 && belowIntersections.length == 0) {
+          return;
+        }
+
+        final boolean edgeCrossesAbove = aboveIntersections.length > 0;
+
+        // This depends on the previous edge that first departs from identicalness.
+        Edge assessEdge = edge;
+        GeoPoint[] assessAboveIntersections;
+        GeoPoint[] assessBelowIntersections;
+        while (true) {
+          assessEdge = assessEdge.previous;
+          assessAboveIntersections = abovePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
+          assessBelowIntersections = belowPlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
+
+          assert !(assessAboveIntersections.length > 0 && assessBelowIntersections.length > 0) : "assess edge that ends in a crossing can't both up and down";
+
+          if (assessAboveIntersections.length == 0 && assessBelowIntersections.length == 0) {
+            continue;
+          }
+          break;
+        }
+        
+        // Basically, we now want to assess whether both edges that come together at this endpoint leave the plane in opposite
+        // directions.  If they do, then we should count it as a crossing; if not, we should not.  We also have to remember that
+        // each edge we look at can also be looked at again if it, too, seems to cross the plane.
+        
+        // To handle the latter situation, we need to know if the other edge will be looked at also, and then we can make
+        // a decision whether to count or not based on that.
+        
+        // Compute the crossing points of this other edge.
+        final GeoPoint[] otherCrossingPoints = plane.findCrossings(planetModel, assessEdge.plane, bound1, bound2, assessEdge.startPlane, assessEdge.endPlane);
+        
+        // Look for a matching endpoint.  If the other endpoint doesn't show up, it is either out of bounds (in which case the
+        // transition won't be counted for that edge), or it is not a crossing for that edge (so, same conclusion).
+        for (final GeoPoint otherCrossingPoint : otherCrossingPoints) {
+          if (otherCrossingPoint.isNumericallyIdentical(assessEdge.endPoint)) {
+            // Found it!
+            // Both edges will try to contribute to the crossing count.  By convention, we'll only include the earlier one.
+            // Since we're the latter point, we exit here in that case.
+            return;
+          }
+        }
+        
+        // Both edges will not count the same point, so we can proceed.  We need to determine the direction of both edges at the
+        // point where they hit the plane.  This may be complicated by the 3D geometry; it may not be safe just to look at the endpoints of the edges
+        // and make an assessment that way, since a single edge can intersect the plane at more than one point.
+        
+        final boolean assessEdgeAbove = assessAboveIntersections.length > 0;
+        if (assessEdgeAbove != edgeCrossesAbove) {
+          crossingCount++;
+        }
+        
+      } else if (crossingPoint.isNumericallyIdentical(edge.endPoint)) {
+        // Figure out if the crossing should be counted.
+        
+        // Does the crossing for this edge go up, or down?  Or can't we tell?
+        final GeoPoint[] aboveIntersections = abovePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
+        final GeoPoint[] belowIntersections = belowPlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
+        
+        assert !(aboveIntersections.length > 0 && belowIntersections.length > 0) : "edge that ends in a crossing can't both up and down";
+        
+        if (aboveIntersections.length == 0 && belowIntersections.length == 0) {
+          return;
+        }
+
+        final boolean edgeCrossesAbove = aboveIntersections.length > 0;
+
+        // This depends on the previous edge that first departs from identicalness.
+        Edge assessEdge = edge;
+        GeoPoint[] assessAboveIntersections;
+        GeoPoint[] assessBelowIntersections;
+        while (true) {
+          assessEdge = assessEdge.next;
+          assessAboveIntersections = abovePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
+          assessBelowIntersections = belowPlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
+
+          assert !(assessAboveIntersections.length > 0 && assessBelowIntersections.length > 0) : "assess edge that ends in a crossing can't both up and down";
+
+          if (assessAboveIntersections.length == 0 && assessBelowIntersections.length == 0) {
+            continue;
+          }
+          break;
+        }
+        
+        // Basically, we now want to assess whether both edges that come together at this endpoint leave the plane in opposite
+        // directions.  If they do, then we should count it as a crossing; if not, we should not.  We also have to remember that
+        // each edge we look at can also be looked at again if it, too, seems to cross the plane.
+        
+        // By definition, we're the earlier plane in this case, so any crossing we detect we must count, by convention.  It is unnecessary
+        // to consider what the other edge does, because when we get to it, it will look back and figure out what we did for this one.
+        
+        // We need to determine the direction of both edges at the
+        // point where they hit the plane.  This may be complicated by the 3D geometry; it may not be safe just to look at the endpoints of the edges
+        // and make an assessment that way, since a single edge can intersect the plane at more than one point.
+
+        final boolean assessEdgeAbove = assessAboveIntersections.length > 0;
+        if (assessEdgeAbove != edgeCrossesAbove) {
+          crossingCount++;
+        }
+
+      } else {
+        crossingCount++;
+      }
+    }
+  }
+  
+  /** Count the number of verifiable edge crossings for a dual-leg journey.
+   */
+  private class DualCrossingEdgeIterator implements EdgeIterator {
+    
+    private boolean isSecondLeg = false;
+    
+    private final Plane testPointPlane;
+    private final Plane testPointAbovePlane;
+    private final Plane testPointBelowPlane;
+    private final Plane travelPlane;
+    private final Plane travelAbovePlane;
+    private final Plane travelBelowPlane;
+    private final Vector thePoint;
+    
+    private final SidedPlane testPointCutoffPlane;
+    private final SidedPlane checkPointCutoffPlane;
+    private final SidedPlane testPointOtherCutoffPlane;
+    private final SidedPlane checkPointOtherCutoffPlane;
+
+    public int crossingCount = 0;
+
+    public DualCrossingEdgeIterator(final Plane testPointPlane, final Plane testPointAbovePlane, final Plane testPointBelowPlane,
+      final Plane travelPlane, final Vector testPoint, final Vector thePoint) {
+      this.testPointPlane = testPointPlane;
+      this.testPointAbovePlane = testPointAbovePlane;
+      this.testPointBelowPlane = testPointBelowPlane;
+      this.travelPlane = travelPlane;
+      this.thePoint = thePoint;
+      this.travelAbovePlane = new Plane(travelPlane, true);
+      this.travelBelowPlane = new Plane(travelPlane, false);
+      this.testPointCutoffPlane = new SidedPlane(thePoint, testPointPlane, testPoint);
+      this.checkPointCutoffPlane = new SidedPlane(testPoint, travelPlane, thePoint);
+      // Now, find the intersection of the check and test point planes.
+      final GeoPoint[] intersectionPoints = travelPlane.findIntersections(planetModel, testPointPlane, testPointCutoffPlane, checkPointCutoffPlane);
+      assert intersectionPoints != null : "couldn't find any intersections";
+      assert intersectionPoints.length != 1 : "wrong number of intersection points";
+      this.testPointOtherCutoffPlane = new SidedPlane(testPoint, testPointPlane, intersectionPoints[0]);
+      this.checkPointOtherCutoffPlane = new SidedPlane(thePoint, travelPlane, intersectionPoints[0]);
+    
+    }
+
+    public void setSecondLeg() {
+      isSecondLeg = true;
+    }
+    
+    @Override
+    public boolean matches(final Edge edge) {
+      // Early exit if the point is on the edge.
+      if (thePoint != null && edge.plane.evaluateIsZero(thePoint) && edge.startPlane.isWithin(thePoint) && edge.endPlane.isWithin(thePoint)) {
+        return false;
+      }
+      final GeoPoint[] crossingPoints;
+      if (isSecondLeg) {
+        crossingPoints = travelPlane.findCrossings(planetModel, edge.plane, checkPointCutoffPlane, checkPointOtherCutoffPlane, edge.startPlane, edge.endPlane);
+      } else {
+        crossingPoints = testPointPlane.findCrossings(planetModel, edge.plane, testPointCutoffPlane, testPointOtherCutoffPlane, edge.startPlane, edge.endPlane);
+      }
+      if (crossingPoints != null) {
+        // We need to handle the endpoint case, which is quite tricky.
+        for (final GeoPoint crossingPoint : crossingPoints) {
+          countCrossingPoint(crossingPoint, edge);
         }
       }
       return true;
     }
 
-    private void countCrossingPoint(final GeoPoint crossingPoint, final Plane plane, final Edge edge) {
+    private void countCrossingPoint(final GeoPoint crossingPoint, final Edge edge) {
+      final Plane plane;
+      final Plane abovePlane;
+      final Plane belowPlane;
+      final SidedPlane bound1;
+      final SidedPlane bound2;
+      if (isSecondLeg) {
+        plane = travelPlane;
+        abovePlane = travelAbovePlane;
+        belowPlane = travelBelowPlane;
+        bound1 = checkPointCutoffPlane;
+        bound2 = checkPointOtherCutoffPlane;
+      } else {
+        plane = testPointPlane;
+        abovePlane = testPointAbovePlane;
+        belowPlane = testPointBelowPlane;
+        bound1 = testPointCutoffPlane;
+        bound2 = testPointOtherCutoffPlane;
+      }
+      
+      // MHL - this code below is temporary code copied from LinearCrossing above
+      
       if (crossingPoint.isNumericallyIdentical(edge.startPoint)) {
         // We have to figure out if this crossing should be counted.
         


[13/26] lucene-solr:master: Add GeoPolygonFactory support for the new shape.

Posted by kw...@apache.org.
Add GeoPolygonFactory support for the new shape.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/478e0e10
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/478e0e10
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/478e0e10

Branch: refs/heads/master
Commit: 478e0e1054f4285634b5e02a2278c48bac0daf90
Parents: bb7d458
Author: Karl Wright <Da...@gmail.com>
Authored: Tue Apr 26 08:56:53 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Tue Apr 26 08:56:53 2016 -0400

----------------------------------------------------------------------
 .../spatial3d/geom/GeoPolygonFactory.java       | 109 ++++++++++++++++++-
 1 file changed, 108 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/478e0e10/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java
index 436b5ac..cd46d42 100755
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java
@@ -113,7 +113,114 @@ public class GeoPolygonFactory {
     }
     throw new IllegalArgumentException("cannot find a point that is inside the polygon "+filteredPointList);
   }
+  
+  /** Use this class to specify a polygon with associated holes.
+   */
+  public static class PolygonDescription {
+    /** The list of points */
+    public final List<? extends GeoPoint> points;
+    /** The list of holes */
+    public final List<? extends PolygonDescription> holes;
+    
+    /** Instantiate the polygon description.
+     * @param points is the list of points.
+     * @param holes is the list of holes.
+     */
+    public PolygonDescription(final List<? extends GeoPoint> points, final List<? extends PolygonDescription> holes) {
+      this.points = points;
+      this.holes = holes;
+    }
+    
+  }
+  
+  /** Create a large GeoPolygon.  This is one which has more than 100 sides and/or may have resolution problems
+   * with very closely spaced points, which often occurs when the polygon was constructed to approximate curves.  No tiling
+   * is done, and intersections and membership are optimized for having large numbers of sides.
+   *
+   * This method does very little checking for legality.  It expects the incoming shapes to not intersect
+   * each other.  The shapes can be disjoint or nested.  If the shapes listed are nested, then we are describing holes.
+   * There is no limit to the depth of holes.  However, if a shape is nested within another it must be explicitly
+   * described as being a child of the other shape.
+   *
+   * Membership in any given shape is described by the clockwise/counterclockwise direction of the points.  The
+   * clockwise direction indicates that a point inside is "in-set", while a counter-clockwise direction implies that
+   * a point inside is "out-of-set".
+   * 
+   * @param planetModel is the planet model.
+   * @param shapesList is the list of polygons we should be making.
+   * @return the GeoPolygon, or null if it cannot be constructed.
+   */
+  public static GeoPolygon makeLargeGeoPolygon(final PlanetModel planetModel,
+    final List<PolygonDescription> shapesList) {
+      
+    // We're going to be building a single-level list of shapes in the end, with a single point that we know to be inside/outside, which is
+    // not on an edge.
+    
+    final List<List<GeoPoint>> pointsList = new ArrayList<>();
     
+    List<GeoPoint> testPointShape = null;
+    for (final PolygonDescription shape : shapesList) {
+      // Convert this shape and its holes to a general list of shapes.  We also need to identify exactly one
+      // legal, non-degenerate shape with no children that we can use to find a test point.  We also optimize
+      // to choose as small as possible a polygon for determining the in-set-ness of the test point.
+      testPointShape = convertPolygon(pointsList, shape, testPointShape);
+    }
+    
+    // If there's no polygon we can use to determine a test point, we throw up.
+    if (testPointShape == null) {
+      throw new IllegalArgumentException("couldn't find a non-degenerate polygon for in-set determination");
+    }
+    
+    // Create a random number generator.  Effectively this furnishes us with a repeatable sequence
+    // of points to use for poles.
+    final Random generator = new Random(1234);
+    for (int counter = 0; counter < 1000000; counter++) {
+      // Pick the next random pole
+      final GeoPoint pole = pickPole(generator, planetModel, testPointShape);
+      // Is it inside or outside?
+      final Boolean isPoleInside = isInsidePolygon(pole, testPointShape);
+      if (isPoleInside != null) {
+        // Legal pole
+        return new GeoComplexPolygon(planetModel, pointsList, pole, isPoleInside);
+      }
+      // If pole choice was illegal, try another one
+    }
+    throw new IllegalArgumentException("cannot find a point that is inside the polygon "+testPointShape);
+
+  }
+
+  /** Convert a polygon description to a list of shapes.  Also locate an optimal shape for evaluating a test point.
+   * @param pointsList is the structure to add new polygons to.
+   * @param shape is the current polygon description.
+   * @param testPointShape is the current best choice for a low-level polygon to evaluate.
+   * @return an updated best-choice for a test point polygon, and update the points list.
+   */
+  private static List<GeoPoint> convertPolygon(final List<List<GeoPoint>> pointsList, final PolygonDescription shape, List<GeoPoint> testPointShape) {
+    // First, remove duplicate points.  If degenerate, just ignore the shape.
+    final List<GeoPoint> filteredPoints = filterPoints(shape.points);
+    if (filteredPoints == null) {
+      return testPointShape;
+    }
+    
+    // Non-degenerate.  Check if this is a candidate for in-set determination.
+    if (shape.holes.size() == 0) {
+      // This shape is a candidate for a test point.
+      if (testPointShape == null || testPointShape.size() > filteredPoints.size()) {
+        testPointShape = filteredPoints;
+      }
+    }
+    
+    pointsList.add(filteredPoints);
+    
+    // Now, do all holes too
+    for (final PolygonDescription hole : shape.holes) {
+      testPointShape = convertPolygon(pointsList, hole, testPointShape);
+    }
+    
+    // Done; return the updated test point shape.
+    return testPointShape;
+  }
+  
   /**
    * Create a GeoPolygon using the specified points and holes and a test point.
    *
@@ -169,7 +276,7 @@ public class GeoPolygonFactory {
    * @param input with input list of points
    * @return the filtered list, or null if we can't get a legit polygon from the input.
    */
-  static List<GeoPoint> filterPoints(final List<GeoPoint> input) {
+  static List<GeoPoint> filterPoints(final List<? extends GeoPoint> input) {
     
     final List<GeoPoint> noIdenticalPoints = new ArrayList<>(input.size());
     


[22/26] lucene-solr:master: Improve path determination code

Posted by kw...@apache.org.
Improve path determination code


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/3d7fd5e1
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/3d7fd5e1
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/3d7fd5e1

Branch: refs/heads/master
Commit: 3d7fd5e1dff41945612a1ac73d3b690db0c5059a
Parents: 72d536c
Author: Karl Wright <Da...@gmail.com>
Authored: Thu Apr 28 15:09:48 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Thu Apr 28 15:09:48 2016 -0400

----------------------------------------------------------------------
 .../spatial3d/geom/GeoComplexPolygon.java       | 48 +++++++++++++-------
 1 file changed, 31 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3d7fd5e1/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
index 914b000..c218776 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
@@ -182,7 +182,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
       
       for (final GeoPoint p : XZIntersectionsYZ) {
         // Travel would be in XZ plane (fixed y) then in YZ (fixed x)
-        final double newDistance = Math.abs(thePoint.x - p.x) + Math.abs(testPoint.y - p.y);
+        final double newDistance = Math.abs(testPoint.y - p.y) + Math.abs(thePoint.x - p.x);
         if (newDistance < bestDistance) {
           bestDistance = newDistance;
           firstLegValue = testPoint.y;
@@ -198,7 +198,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
       }
       for (final GeoPoint p : XZIntersectionsXY) {
         // Travel would be in XZ plane (fixed y) then in XY (fixed z)
-        final double newDistance = Math.abs(thePoint.z - p.z) + Math.abs(testPoint.y - p.y);
+        final double newDistance = Math.abs(testPoint.y - p.y) + Math.abs(thePoint.z - p.z);
         if (newDistance < bestDistance) {
           bestDistance = newDistance;
           firstLegValue = testPoint.y;
@@ -214,7 +214,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
       }
       for (final GeoPoint p : YZIntersectionsXZ) {
         // Travel would be in YZ plane (fixed x) then in XZ (fixed y)
-        final double newDistance = Math.abs(thePoint.y - p.y) + Math.abs(testPoint.x - p.x);
+        final double newDistance = Math.abs(testPoint.x - p.x) + Math.abs(thePoint.y - p.y);
         if (newDistance < bestDistance) {
           bestDistance = newDistance;
           firstLegValue = testPoint.x;
@@ -230,7 +230,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
       }
       for (final GeoPoint p : YZIntersectionsXY) {
         // Travel would be in YZ plane (fixed x) then in XY (fixed z)
-        final double newDistance = Math.abs(thePoint.z - p.z) + Math.abs(testPoint.x - p.x);
+        final double newDistance = Math.abs(testPoint.x - p.x) + Math.abs(thePoint.z - p.z);
         if (newDistance < bestDistance) {
           bestDistance = newDistance;
           firstLegValue = testPoint.x;
@@ -246,7 +246,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
       }
       for (final GeoPoint p : XYIntersectionsYZ) {
         // Travel would be in XY plane (fixed z) then in YZ (fixed x)
-        final double newDistance = Math.abs(thePoint.x - p.x) + Math.abs(testPoint.z - p.z);
+        final double newDistance = Math.abs(testPoint.z - p.z) + Math.abs(thePoint.x - p.x);
         if (newDistance < bestDistance) {
           bestDistance = newDistance;
           firstLegValue = testPoint.z;
@@ -262,7 +262,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
       }
       for (final GeoPoint p : XYIntersectionsXZ) {
         // Travel would be in XY plane (fixed z) then in XZ (fixed y)
-        final double newDistance = Math.abs(thePoint.y - p.y) + Math.abs(testPoint.z - p.z);
+        final double newDistance = Math.abs(testPoint.z - p.z) + Math.abs(thePoint.y - p.y);
         if (newDistance < bestDistance) {
           bestDistance = newDistance;
           firstLegValue = testPoint.z;
@@ -279,6 +279,8 @@ class GeoComplexPolygon extends GeoBasePolygon {
 
       assert bestDistance < Double.MAX_VALUE : "Couldn't find an intersection point of any kind";
       
+      System.err.println("Best distance: "+bestDistance);
+      
       final DualCrossingEdgeIterator edgeIterator = new DualCrossingEdgeIterator(firstLegPlane, firstLegAbovePlane, firstLegBelowPlane, secondLegPlane, testPoint, thePoint, intersectionPoint);
       if (!firstLegTree.traverse(edgeIterator, firstLegValue)) {
         return true;
@@ -906,27 +908,37 @@ class GeoComplexPolygon extends GeoBasePolygon {
       this.travelPlane = travelPlane;
       this.thePoint = thePoint;
       this.intersectionPoint = intersectionPoint;
-        
-      this.testPointCutoffPlane = new SidedPlane(thePoint, testPointPlane, testPoint);
-      this.checkPointCutoffPlane = new SidedPlane(testPoint, travelPlane, thePoint);
+      
+      assert !testPoint.isNumericallyIdentical(intersectionPoint) : "test point is the same as intersection point";
+      assert !thePoint.isNumericallyIdentical(intersectionPoint) : "check point is same is intersection point";
+
+      this.testPointCutoffPlane = new SidedPlane(intersectionPoint, testPointPlane, testPoint);
+      this.checkPointCutoffPlane = new SidedPlane(intersectionPoint, travelPlane, thePoint);
 
       // Convert travel plane to a sided plane
       this.testPointOtherCutoffPlane = new SidedPlane(testPoint, travelPlane, travelPlane.D);
       // Convert testPoint plane to a sided plane
       this.checkPointOtherCutoffPlane = new SidedPlane(thePoint, testPointPlane, testPointPlane.D);
-        
+
+      // Sanity check
+      assert testPointCutoffPlane.isWithin(intersectionPoint) : "intersection must be within testPointCutoffPlane";
+      assert testPointOtherCutoffPlane.isWithin(intersectionPoint) : "intersection must be within testPointOtherCutoffPlane";
+      assert checkPointCutoffPlane.isWithin(intersectionPoint) : "intersection must be within checkPointCutoffPlane";
+      assert checkPointOtherCutoffPlane.isWithin(intersectionPoint) : "intersection must be within checkPointOtherCutoffPlane";
+      
       // Figure out which of the above/below planes are inside vs. outside.  To do this,
       // we look for the point that is within the bounds of the testPointPlane and travelPlane.  The two sides that intersected there are the inside
       // borders.
       final Plane travelAbovePlane = new Plane(travelPlane, true);
       final Plane travelBelowPlane = new Plane(travelPlane, false);
-      final GeoPoint[] aboveAbove = travelAbovePlane.findIntersections(planetModel, testPointAbovePlane, testPointCutoffPlane, testPointOtherCutoffPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane);
+      
+      final GeoPoint[] aboveAbove = travelAbovePlane.findIntersections(planetModel, testPointAbovePlane, testPointOtherCutoffPlane, checkPointOtherCutoffPlane);
       assert aboveAbove != null : "Above + above should not be coplanar";
-      final GeoPoint[] aboveBelow = travelAbovePlane.findIntersections(planetModel, testPointBelowPlane, testPointCutoffPlane, testPointOtherCutoffPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane);
+      final GeoPoint[] aboveBelow = travelAbovePlane.findIntersections(planetModel, testPointBelowPlane, testPointOtherCutoffPlane, checkPointOtherCutoffPlane);
       assert aboveBelow != null : "Above + below should not be coplanar";
-      final GeoPoint[] belowBelow = travelBelowPlane.findIntersections(planetModel, testPointBelowPlane, testPointCutoffPlane, testPointOtherCutoffPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane);
+      final GeoPoint[] belowBelow = travelBelowPlane.findIntersections(planetModel, testPointBelowPlane, testPointOtherCutoffPlane, checkPointOtherCutoffPlane);
       assert belowBelow != null : "Below + below should not be coplanar";
-      final GeoPoint[] belowAbove = travelBelowPlane.findIntersections(planetModel, testPointAbovePlane, testPointCutoffPlane, testPointOtherCutoffPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane);
+      final GeoPoint[] belowAbove = travelBelowPlane.findIntersections(planetModel, testPointAbovePlane, testPointOtherCutoffPlane, checkPointOtherCutoffPlane);
       assert belowAbove != null : "Below + above should not be coplanar";
 
       assert ((aboveAbove.length > 0)?1:0) + ((aboveBelow.length > 0)?1:0) + ((belowBelow.length > 0)?1:0) + ((belowAbove.length > 0)?1:0) == 1 : "Can be exactly one inside point, instead was: aa="+aboveAbove.length+" ab=" + aboveBelow.length+" bb="+ belowBelow.length+" ba=" + belowAbove.length;
@@ -1079,7 +1091,8 @@ class GeoComplexPolygon extends GeoBasePolygon {
           assessOutsideTestPointIntersections = testPointOutsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
           assessOutsideTravelIntersections = travelOutsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
 
-          assert !(assessInsideTestPointIntersections.length + assessInsideTravelIntersections.length > 0 && assessOutsideTestPointIntersections.length + assessOutsideTravelIntersections.length > 0) : "assess edge that ends in a crossing can't both up and down";
+          // An edge can cross both outside and inside, because of the corner.  But it can be considered to cross the inside ONLY if it crosses either of the inside edges.
+          //assert !(assessInsideTestPointIntersections.length + assessInsideTravelIntersections.length > 0 && assessOutsideTestPointIntersections.length + assessOutsideTravelIntersections.length > 0) : "assess edge that ends in a crossing can't both up and down";
 
           if (assessInsideTestPointIntersections.length + assessInsideTravelIntersections.length == 0 && assessOutsideTestPointIntersections.length + assessOutsideTravelIntersections.length == 0) {
             continue;
@@ -1126,8 +1139,9 @@ class GeoComplexPolygon extends GeoBasePolygon {
         final GeoPoint[] insideTravelPlaneIntersections = travelInsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane, insideTravelCutoffPlane);
         final GeoPoint[] outsideTestPointPlaneIntersections = testPointOutsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
         final GeoPoint[] outsideTravelPlaneIntersections = travelOutsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
-          
-        assert !(insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length > 0 && outsideTestPointPlaneIntersections.length + outsideTravelPlaneIntersections.length > 0) : "edge that ends in a crossing can't both up and down";
+        
+        // An edge can cross both outside and inside, because of the corner.  But it can be considered to cross the inside ONLY if it crosses either of the inside edges.
+        //assert !(insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length > 0 && outsideTestPointPlaneIntersections.length + outsideTravelPlaneIntersections.length > 0) : "edge that ends in a crossing can't go both up and down: insideTestPointPlaneIntersections: "+insideTestPointPlaneIntersections.length+" insideTravelPlaneIntersections: "+insideTravelPlaneIntersections.length+" outsideTestPointPlaneIntersections: "+outsideTestPointPlaneIntersections.length+" outsideTravelPlaneIntersections: "+outsideTravelPlaneIntersections.length;
           
         if (insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length == 0 && outsideTestPointPlaneIntersections.length + outsideTravelPlaneIntersections.length == 0) {
           return;


[17/26] lucene-solr:master: More robust logic for picking the intersection point and path

Posted by kw...@apache.org.
More robust logic for picking the intersection point and path


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/9dd7921f
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/9dd7921f
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/9dd7921f

Branch: refs/heads/master
Commit: 9dd7921f9df26ee8374029154936a2279f08891a
Parents: be0fff0
Author: Karl Wright <Da...@gmail.com>
Authored: Wed Apr 27 13:54:28 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Wed Apr 27 13:54:28 2016 -0400

----------------------------------------------------------------------
 .../spatial3d/geom/GeoComplexPolygon.java       | 236 +++++++++++++------
 .../spatial3d/geom/GeoPolygonFactory.java       |   7 +
 .../lucene/spatial3d/geom/GeoPolygonTest.java   |   9 +-
 3 files changed, 183 insertions(+), 69 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9dd7921f/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
index 47ca961..d8c8e75 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
@@ -35,9 +35,9 @@ import java.util.Map;
  */
 class GeoComplexPolygon extends GeoBasePolygon {
   
-  private final XTree xTree = new XTree();
-  private final YTree yTree = new YTree();
-  private final ZTree zTree = new ZTree();
+  private final Tree xTree = new XTree();
+  private final Tree yTree = new YTree();
+  private final Tree zTree = new ZTree();
   
   private final boolean testPointInSet;
   private final GeoPoint testPoint;
@@ -125,11 +125,6 @@ class GeoComplexPolygon extends GeoBasePolygon {
       return testPointInSet;
     }
     
-    // Choose our navigation route!
-    final double xDelta = Math.abs(thePoint.x - testPoint.x);
-    final double yDelta = Math.abs(thePoint.y - testPoint.y);
-    final double zDelta = Math.abs(thePoint.z - testPoint.z);
-    
     // If we're right on top of any of the test planes, we navigate solely on that plane.
     if (testPointXZPlane.evaluateIsZero(thePoint)) {
       // Use the XZ plane exclusively.
@@ -160,50 +155,141 @@ class GeoComplexPolygon extends GeoBasePolygon {
       return ((crossingEdgeIterator.crossingCount & 1) == 0)?testPointInSet:!testPointInSet;
     } else {
       
-      // We need to use two planes to get there.  We can use any two planes, and order doesn't matter.
-      // The best to pick are the ones with the shortest overall distance.
-      if (xDelta + yDelta <= xDelta + zDelta && xDelta + yDelta <= yDelta + zDelta) {
-        // Travel in X and Y
-        // We'll do this using the testPointYZPlane, and create a travel plane for the right XZ plane.
-        final Plane travelPlane = new Plane(0.0, 1.0, 0.0, -thePoint.y);
-        final DualCrossingEdgeIterator edgeIterator = new DualCrossingEdgeIterator(testPointYZPlane, testPointYZAbovePlane, testPointYZBelowPlane, travelPlane, testPoint, thePoint);
-        if (!xTree.traverse(edgeIterator, testPoint.x, testPoint.x)) {
-          return true;
+      // We need to use two planes to get there.  We don't know which two planes will do it but we can figure it out.
+      final Plane travelPlaneFixedX = new Plane(1.0, 0.0, 0.0, -thePoint.x);
+      final Plane travelPlaneFixedY = new Plane(0.0, 1.0, 0.0, -thePoint.y);
+      final Plane travelPlaneFixedZ = new Plane(0.0, 0.0, 1.0, -thePoint.z);
+
+      // Find the intersection points for each one of these and the complementary test point planes.
+      final GeoPoint[] XZIntersectionsYZ = travelPlaneFixedX.findIntersections(planetModel, testPointYZPlane);
+      final GeoPoint[] XZIntersectionsXY = travelPlaneFixedX.findIntersections(planetModel, testPointXYPlane);
+      final GeoPoint[] YZIntersectionsXZ = travelPlaneFixedY.findIntersections(planetModel, testPointXZPlane);
+      final GeoPoint[] YZIntersectionsXY = travelPlaneFixedY.findIntersections(planetModel, testPointXYPlane);
+      final GeoPoint[] XYIntersectionsYZ = travelPlaneFixedZ.findIntersections(planetModel, testPointYZPlane);
+      final GeoPoint[] XYIntersectionsXZ = travelPlaneFixedZ.findIntersections(planetModel, testPointXZPlane);
+
+      // There will be multiple intersection points found.  We choose the one that has the lowest total distance, as measured in delta X, delta Y, and delta Z.
+      double bestDistance = Double.MAX_VALUE;
+      double firstLegValue = 0.0;
+      double secondLegValue = 0.0;
+      Plane firstLegPlane = null;
+      Plane firstLegAbovePlane = null;
+      Plane firstLegBelowPlane = null;
+      Plane secondLegPlane = null;
+      Tree firstLegTree = null;
+      Tree secondLegTree = null;
+      GeoPoint intersectionPoint = null;
+      
+      for (final GeoPoint p : XZIntersectionsYZ) {
+        // Travel would be in XZ plane (fixed y) then in YZ (fixed x)
+        final double newDistance = Math.abs(thePoint.x - p.x) + Math.abs(testPoint.y - p.y);
+        if (newDistance < bestDistance) {
+          bestDistance = newDistance;
+          firstLegValue = testPoint.y;
+          secondLegValue = thePoint.x;
+          firstLegPlane = testPointYZPlane;
+          firstLegAbovePlane = testPointYZAbovePlane;
+          firstLegBelowPlane = testPointYZBelowPlane;
+          secondLegPlane = travelPlaneFixedX;
+          firstLegTree = xTree;
+          secondLegTree = yTree;
+          intersectionPoint = p;
         }
-        edgeIterator.setSecondLeg();
-        if (!yTree.traverse(edgeIterator, thePoint.y, thePoint.y)) {
-          return true;
+      }
+      for (final GeoPoint p : XZIntersectionsXY) {
+        // Travel would be in XZ plane (fixed y) then in XY (fixed z)
+        final double newDistance = Math.abs(thePoint.z - p.z) + Math.abs(testPoint.y - p.y);
+        if (newDistance < bestDistance) {
+          bestDistance = newDistance;
+          firstLegValue = testPoint.y;
+          secondLegValue = thePoint.z;
+          firstLegPlane = testPointXYPlane;
+          firstLegAbovePlane = testPointXYAbovePlane;
+          firstLegBelowPlane = testPointXYBelowPlane;
+          secondLegPlane = travelPlaneFixedX;
+          firstLegTree = yTree;
+          secondLegTree = zTree;
+          intersectionPoint = p;
         }
-        return ((edgeIterator.crossingCount  & 1) == 0)?testPointInSet:!testPointInSet;
-      } else if (xDelta + zDelta <= xDelta + yDelta && xDelta + zDelta <= zDelta + yDelta) {
-        // Travel in X and Z
-        // We'll do this using the testPointXYPlane, and create a travel plane for the right YZ plane.
-        final Plane travelPlane = new Plane(1.0, 0.0, 0.0, -thePoint.x);
-        final DualCrossingEdgeIterator edgeIterator = new DualCrossingEdgeIterator(testPointXYPlane, testPointXYAbovePlane, testPointXYBelowPlane, travelPlane, testPoint, thePoint);
-        if (!zTree.traverse(edgeIterator, testPoint.z, testPoint.z)) {
-          return true;
+      }
+      for (final GeoPoint p : YZIntersectionsXZ) {
+        // Travel would be in YZ plane (fixed x) then in XZ (fixed y)
+        final double newDistance = Math.abs(thePoint.y - p.y) + Math.abs(testPoint.x - p.x);
+        if (newDistance < bestDistance) {
+          bestDistance = newDistance;
+          firstLegValue = testPoint.x;
+          secondLegValue = thePoint.y;
+          firstLegPlane = testPointXZPlane;
+          firstLegAbovePlane = testPointXZAbovePlane;
+          firstLegBelowPlane = testPointXZBelowPlane;
+          secondLegPlane = travelPlaneFixedY;
+          firstLegTree = xTree;
+          secondLegTree = yTree;
+          intersectionPoint = p;
         }
-        edgeIterator.setSecondLeg();
-        if (!xTree.traverse(edgeIterator, thePoint.x, thePoint.x)) {
-          return true;
+      }
+      for (final GeoPoint p : YZIntersectionsXY) {
+        // Travel would be in YZ plane (fixed x) then in XY (fixed z)
+        final double newDistance = Math.abs(thePoint.z - p.z) + Math.abs(testPoint.x - p.x);
+        if (newDistance < bestDistance) {
+          bestDistance = newDistance;
+          firstLegValue = testPoint.x;
+          secondLegValue = thePoint.z;
+          firstLegPlane = testPointXYPlane;
+          firstLegAbovePlane = testPointXYAbovePlane;
+          firstLegBelowPlane = testPointXYBelowPlane;
+          secondLegPlane = travelPlaneFixedX;
+          firstLegTree = xTree;
+          secondLegTree = zTree;
+          intersectionPoint = p;
         }
-        return ((edgeIterator.crossingCount & 1) == 0)?testPointInSet:!testPointInSet;
-      } else if (yDelta + zDelta <= xDelta + yDelta && yDelta + zDelta <= xDelta + zDelta) {
-        // Travel in Y and Z
-        // We'll do this using the testPointXZPlane, and create a travel plane for the right XY plane.
-        final Plane travelPlane = new Plane(0.0, 0.0, 1.0, -thePoint.z);
-        final DualCrossingEdgeIterator edgeIterator = new DualCrossingEdgeIterator(testPointXZPlane, testPointXZAbovePlane, testPointXZBelowPlane, travelPlane, testPoint, thePoint);
-        if (!yTree.traverse(edgeIterator, testPoint.y, testPoint.y)) {
-          return true;
+      }
+      for (final GeoPoint p : XYIntersectionsYZ) {
+        // Travel would be in XY plane (fixed z) then in YZ (fixed x)
+        final double newDistance = Math.abs(thePoint.x - p.x) + Math.abs(testPoint.z - p.z);
+        if (newDistance < bestDistance) {
+          bestDistance = newDistance;
+          firstLegValue = testPoint.z;
+          secondLegValue = thePoint.x;
+          firstLegPlane = testPointYZPlane;
+          firstLegAbovePlane = testPointYZAbovePlane;
+          firstLegBelowPlane = testPointYZBelowPlane;
+          secondLegPlane = travelPlaneFixedZ;
+          firstLegTree = zTree;
+          secondLegTree = xTree;
+          intersectionPoint = p;
         }
-        edgeIterator.setSecondLeg();
-        if (!zTree.traverse(edgeIterator, thePoint.z, thePoint.z)) {
-          return true;
+      }
+      for (final GeoPoint p : XYIntersectionsXZ) {
+        // Travel would be in XY plane (fixed z) then in XZ (fixed y)
+        final double newDistance = Math.abs(thePoint.y - p.y) + Math.abs(testPoint.z - p.z);
+        if (newDistance < bestDistance) {
+          bestDistance = newDistance;
+          firstLegValue = testPoint.z;
+          secondLegValue = thePoint.y;
+          firstLegPlane = testPointXZPlane;
+          firstLegAbovePlane = testPointXZAbovePlane;
+          firstLegBelowPlane = testPointXZBelowPlane;
+          secondLegPlane = travelPlaneFixedZ;
+          firstLegTree = zTree;
+          secondLegTree = yTree;
+          intersectionPoint = p;
         }
-        return ((edgeIterator.crossingCount & 1) == 0)?testPointInSet:!testPointInSet;
       }
+
+      assert bestDistance < Double.MAX_VALUE : "Couldn't find an intersection point of any kind";
+      
+      final DualCrossingEdgeIterator edgeIterator = new DualCrossingEdgeIterator(firstLegPlane, firstLegAbovePlane, firstLegBelowPlane, secondLegPlane, testPoint, thePoint, intersectionPoint);
+      if (!firstLegTree.traverse(edgeIterator, firstLegValue, firstLegValue)) {
+        return true;
+      }
+      edgeIterator.setSecondLeg();
+      if (!secondLegTree.traverse(edgeIterator, secondLegValue, secondLegValue)) {
+        return true;
+      }
+      return ((edgeIterator.crossingCount  & 1) == 0)?testPointInSet:!testPointInSet;
+
     }
-    return false;
   }
   
   @Override
@@ -298,6 +384,8 @@ class GeoComplexPolygon extends GeoBasePolygon {
       this.startPlane =  new SidedPlane(endPoint, plane, startPoint);
       this.endPlane = new SidedPlane(startPoint, plane, endPoint);
       this.planeBounds = new XYZBounds();
+      this.planeBounds.addPoint(startPoint);
+      this.planeBounds.addPoint(endPoint);
       this.plane.recordBounds(pm, this.planeBounds, this.startPlane, this.endPlane);
     }
   }
@@ -372,25 +460,25 @@ class GeoComplexPolygon extends GeoBasePolygon {
     public void add(final Edge newEdge, final AddComparator edgeComparator) {
       Node currentNode = this;
       while (true) {
-        final int result = edgeComparator.compare(edge, newEdge);
+        final int result = edgeComparator.compare(currentNode.edge, newEdge);
         if (result < 0) {
-          if (lesser == null) {
-            lesser = new Node(newEdge);
+          if (currentNode.lesser == null) {
+            currentNode.lesser = new Node(newEdge);
             return;
           }
-          currentNode = lesser;
+          currentNode = currentNode.lesser;
         } else if (result > 0) {
-          if (greater == null) {
-            greater = new Node(newEdge);
+          if (currentNode.greater == null) {
+            currentNode.greater = new Node(newEdge);
             return;
           }
-          currentNode = greater;
+          currentNode = currentNode.greater;
         } else {
-          if (overlaps == null) {
-            overlaps = new Node(newEdge);
+          if (currentNode.overlaps == null) {
+            currentNode.overlaps = new Node(newEdge);
             return;
           }
-          currentNode = overlaps;
+          currentNode = currentNode.overlaps;
         }
       }
     }
@@ -400,28 +488,39 @@ class GeoComplexPolygon extends GeoBasePolygon {
       while (currentNode != null) {
         final int result = edgeComparator.compare(currentNode.edge, minValue, maxValue);
         if (result < 0) {
-          currentNode = lesser;
+          currentNode = currentNode.lesser;
         } else if (result > 0) {
-          currentNode = greater;
+          currentNode = currentNode.greater;
         } else {
-          if (!edgeIterator.matches(edge)) {
+          if (!edgeIterator.matches(currentNode.edge)) {
             return false;
           }
-          currentNode = overlaps;
+          currentNode = currentNode.overlaps;
         }
       }
       return true;
     }
   }
   
+  /** An interface describing a tree.
+   */
+  private static interface Tree {
+    
+    public void add(final Edge edge);
+    
+    public boolean traverse(final EdgeIterator edgeIterator, final double minValue, final double maxValue);
+
+  }
+  
   /** This is the z-tree.
    */
-  private static class ZTree implements TraverseComparator, AddComparator {
+  private static class ZTree implements Tree, TraverseComparator, AddComparator {
     public Node rootNode = null;
     
     public ZTree() {
     }
     
+    @Override
     public void add(final Edge edge) {
       if (rootNode == null) {
         rootNode = new Node(edge);
@@ -430,6 +529,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
       }
     }
     
+    @Override
     public boolean traverse(final EdgeIterator edgeIterator, final double minValue, final double maxValue) {
       if (rootNode == null) {
         return true;
@@ -461,12 +561,13 @@ class GeoComplexPolygon extends GeoBasePolygon {
   
   /** This is the y-tree.
    */
-  private static class YTree implements TraverseComparator, AddComparator {
+  private static class YTree implements Tree, TraverseComparator, AddComparator {
     public Node rootNode = null;
     
     public YTree() {
     }
     
+    @Override
     public void add(final Edge edge) {
       if (rootNode == null) {
         rootNode = new Node(edge);
@@ -475,6 +576,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
       }
     }
     
+    @Override
     public boolean traverse(final EdgeIterator edgeIterator, final double minValue, final double maxValue) {
       if (rootNode == null) {
         return true;
@@ -506,12 +608,13 @@ class GeoComplexPolygon extends GeoBasePolygon {
 
   /** This is the x-tree.
    */
-  private static class XTree implements TraverseComparator, AddComparator {
+  private static class XTree implements Tree, TraverseComparator, AddComparator {
     public Node rootNode = null;
     
     public XTree() {
     }
     
+    @Override
     public void add(final Edge edge) {
       if (rootNode == null) {
         rootNode = new Node(edge);
@@ -520,6 +623,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
       }
     }
     
+    @Override
     public boolean traverse(final EdgeIterator edgeIterator, final double minValue, final double maxValue) {
       if (rootNode == null) {
         return true;
@@ -752,17 +856,15 @@ class GeoComplexPolygon extends GeoBasePolygon {
     public int crossingCount = 0;
 
     public DualCrossingEdgeIterator(final Plane testPointPlane, final Plane testPointAbovePlane, final Plane testPointBelowPlane,
-      final Plane travelPlane, final Vector testPoint, final Vector thePoint) {
+      final Plane travelPlane, final Vector testPoint, final Vector thePoint, final GeoPoint intersectionPoint) {
       this.testPointPlane = testPointPlane;
       this.travelPlane = travelPlane;
       this.thePoint = thePoint;
+      this.intersectionPoint = intersectionPoint;
+        
       this.testPointCutoffPlane = new SidedPlane(thePoint, testPointPlane, testPoint);
       this.checkPointCutoffPlane = new SidedPlane(testPoint, travelPlane, thePoint);
-      // Now, find the intersection of the check and test point planes.
-      final GeoPoint[] intersectionPoints = travelPlane.findIntersections(planetModel, testPointPlane, testPointCutoffPlane, checkPointCutoffPlane);
-      assert intersectionPoints != null : "couldn't find any intersections";
-      assert intersectionPoints.length != 1 : "wrong number of intersection points";
-      this.intersectionPoint = intersectionPoints[0];
+        
       this.testPointOtherCutoffPlane = new SidedPlane(testPoint, testPointPlane, intersectionPoint);
       this.checkPointOtherCutoffPlane = new SidedPlane(thePoint, travelPlane, intersectionPoint);
         

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9dd7921f/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java
index cd46d42..81aaa65 100755
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java
@@ -124,6 +124,13 @@ public class GeoPolygonFactory {
     
     /** Instantiate the polygon description.
      * @param points is the list of points.
+     */
+    public PolygonDescription(final List<? extends GeoPoint> points) {
+      this(points, new ArrayList<>());
+    }
+
+    /** Instantiate the polygon description.
+     * @param points is the list of points.
      * @param holes is the list of holes.
      */
     public PolygonDescription(final List<? extends GeoPoint> points, final List<? extends PolygonDescription> holes) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9dd7921f/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
index d76ae4e..a196495 100755
--- a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
+++ b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
@@ -84,9 +84,7 @@ public class GeoPolygonTest {
       originalPoints.add(point1);
       originalPoints.add(point3);
       originalPoints.add(point4);
-      System.err.println("Before: "+originalPoints);
       final List<GeoPoint> filteredPoints = GeoPolygonFactory.filterEdges(GeoPolygonFactory.filterPoints(originalPoints), 0.0);
-      System.err.println("After: "+filteredPoints);
       assertEquals(3, filteredPoints.size());
       assertEquals(point5, filteredPoints.get(0));
       assertEquals(point1, filteredPoints.get(1));
@@ -100,6 +98,7 @@ public class GeoPolygonTest {
     GeoPolygon c;
     GeoPoint gp;
     List<GeoPoint> points;
+    List<GeoPolygonFactory.PolygonDescription> shapes;
 
     // Points go counterclockwise, so 
     points = new ArrayList<GeoPoint>();
@@ -115,6 +114,12 @@ public class GeoPolygonTest {
     gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.5);
     assertTrue(!c.isWithin(gp));
 
+    shapes = new ArrayList<>();
+    shapes.add(new GeoPolygonFactory.PolygonDescription(points));
+    
+    c = GeoPolygonFactory.makeLargeGeoPolygon(PlanetModel.SPHERE, shapes);
+    assertTrue(!c.isWithin(gp));
+    
     // Now, go clockwise
     points = new ArrayList<GeoPoint>();
     points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.4));


[09/26] lucene-solr:master: Separate the above and below planes everywhere so we can compute intersections and bounds.

Posted by kw...@apache.org.
Separate the above and below planes everywhere so we can compute intersections and bounds.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/4105fda9
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/4105fda9
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/4105fda9

Branch: refs/heads/master
Commit: 4105fda9c1bc0e8810494425ce69207ac53807f5
Parents: 641d221
Author: Karl Wright <Da...@gmail.com>
Authored: Mon Apr 25 15:50:45 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Mon Apr 25 15:50:45 2016 -0400

----------------------------------------------------------------------
 .../spatial3d/geom/GeoComplexPolygon.java       | 43 ++++++++++++++------
 1 file changed, 31 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4105fda9/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
index 9b6f670..a3ef6ab 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
@@ -43,8 +43,14 @@ class GeoComplexPolygon extends GeoBasePolygon {
   private final GeoPoint testPoint;
   
   private final Plane testPointXZPlane;
+  private final Plane testPointXZAbovePlane;
+  private final Plane testPointXZBelowPlane;
   private final Plane testPointYZPlane;
+  private final Plane testPointYZAbovePlane;
+  private final Plane testPointYZBelowPlane;
   private final Plane testPointXYPlane;
+  private final Plane testPointXYAbovePlane;
+  private final Plane testPointXYBelowPlane;
   
   private final GeoPoint[] edgePoints;
   private final Edge[] shapeStartEdges;
@@ -69,6 +75,13 @@ class GeoComplexPolygon extends GeoBasePolygon {
     this.testPointYZPlane = new Plane(1.0, 0.0, 0.0, -testPoint.x);
     this.testPointXYPlane = new Plane(0.0, 0.0, 1.0, -testPoint.z);
     
+    this.testPointXZAbovePlane = new Plane(testPointXZPlane, true);
+    this.testPointXZBelowPlane = new Plane(testPointXZPlane, false);
+    this.testPointYZAbovePlane = new Plane(testPointYZPlane, true);
+    this.testPointYZBelowPlane = new Plane(testPointYZPlane, false);
+    this.testPointXYAbovePlane = new Plane(testPointXYPlane, true);
+    this.testPointXYBelowPlane = new Plane(testPointXYPlane, false);
+
     this.edgePoints = new GeoPoint[pointsList.size()];
     this.shapeStartEdges = new Edge[pointsList.size()];
     int edgePointIndex = 0;
@@ -122,7 +135,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
       // Use the XZ plane exclusively.
       final SidedPlane testPointCutoff =  new SidedPlane(thePoint, testPointXZPlane, testPoint);
       final SidedPlane checkPointCutoff = new SidedPlane(testPoint, testPointXZPlane, thePoint);
-      final CrossingEdgeIterator crossingEdgeIterator = new CrossingEdgeIterator(testPointXZPlane, testPointCutoff, checkPointCutoff, thePoint);
+      final CrossingEdgeIterator crossingEdgeIterator = new CrossingEdgeIterator(testPointXZPlane, testPointXZAbovePlane, testPointXZBelowPlane, testPointCutoff, checkPointCutoff, thePoint);
       // Traverse our way from the test point to the check point.  Use the y tree because that's fixed.
       if (!yTree.traverse(crossingEdgeIterator, testPoint.y, testPoint.y)) {
         // Endpoint is on edge
@@ -133,7 +146,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
       // Use the YZ plane exclusively.
       final SidedPlane testPointCutoff =  new SidedPlane(thePoint, testPointYZPlane, testPoint);
       final SidedPlane checkPointCutoff = new SidedPlane(testPoint, testPointYZPlane, thePoint);
-      final CrossingEdgeIterator crossingEdgeIterator = new CrossingEdgeIterator(testPointYZPlane, testPointCutoff, checkPointCutoff, thePoint);
+      final CrossingEdgeIterator crossingEdgeIterator = new CrossingEdgeIterator(testPointYZPlane, testPointYZAbovePlane, testPointYZBelowPlane, testPointCutoff, checkPointCutoff, thePoint);
       // Traverse our way from the test point to the check point.  Use the x tree because that's fixed.
       if (!xTree.traverse(crossingEdgeIterator, testPoint.x, testPoint.x)) {
         // Endpoint is on edge
@@ -144,7 +157,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
       // Use the XY plane exclusively.
       final SidedPlane testPointCutoff =  new SidedPlane(thePoint, testPointXYPlane, testPoint);
       final SidedPlane checkPointCutoff = new SidedPlane(testPoint, testPointXYPlane, thePoint);
-      final CrossingEdgeIterator crossingEdgeIterator = new CrossingEdgeIterator(testPointXYPlane, testPointCutoff, checkPointCutoff, thePoint);
+      final CrossingEdgeIterator crossingEdgeIterator = new CrossingEdgeIterator(testPointXYPlane, testPointXYAbovePlane, testPointXYBelowPlane, testPointCutoff, checkPointCutoff, thePoint);
       // Traverse our way from the test point to the check point.  Use the z tree because that's fixed.
       if (!zTree.traverse(crossingEdgeIterator, testPoint.z, testPoint.z)) {
         // Endpoint is on edge
@@ -159,6 +172,8 @@ class GeoComplexPolygon extends GeoBasePolygon {
         // Travel in X and Y
         // We'll do this using the testPointYZPlane, and create a travel plane for the right XZ plane.
         final Plane travelPlane = new Plane(0.0, 1.0, 0.0, -thePoint.y);
+        final Plane travelAbovePlane = new Plane(travelPlane, true);
+        final Plane travelBelowPlane = new Plane(travelPlane, false);
         // We need cutoff planes for both legs.
         final SidedPlane testPointCutoffPlane = new SidedPlane(thePoint, testPointYZPlane, testPoint);
         final SidedPlane checkPointCutoffPlane = new SidedPlane(testPoint, travelPlane, thePoint);
@@ -170,9 +185,9 @@ class GeoComplexPolygon extends GeoBasePolygon {
         final SidedPlane checkPointOtherCutoffPlane = new SidedPlane(thePoint, travelPlane, intersectionPoints[0]);
         // Note: we need to handle the cases where end point of the leg sits on an edge!
         // MHL
-        final CrossingEdgeIterator testPointEdgeIterator = new CrossingEdgeIterator(testPointYZPlane, testPointCutoffPlane, testPointOtherCutoffPlane, null);
+        final CrossingEdgeIterator testPointEdgeIterator = new CrossingEdgeIterator(testPointYZPlane, testPointYZAbovePlane, testPointYZBelowPlane, testPointCutoffPlane, testPointOtherCutoffPlane, null);
         xTree.traverse(testPointEdgeIterator, testPoint.x, testPoint.x);
-        final CrossingEdgeIterator checkPointEdgeIterator = new CrossingEdgeIterator(travelPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane, thePoint);
+        final CrossingEdgeIterator checkPointEdgeIterator = new CrossingEdgeIterator(travelPlane, travelAbovePlane, travelBelowPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane, thePoint);
         if (!yTree.traverse(checkPointEdgeIterator, thePoint.y, thePoint.y)) {
           // Endpoint is on edge
           return true;
@@ -182,6 +197,8 @@ class GeoComplexPolygon extends GeoBasePolygon {
         // Travel in X and Z
         // We'll do this using the testPointXYPlane, and create a travel plane for the right YZ plane.
         final Plane travelPlane = new Plane(1.0, 0.0, 0.0, -thePoint.x);
+        final Plane travelAbovePlane = new Plane(travelPlane, true);
+        final Plane travelBelowPlane = new Plane(travelPlane, false);
         // We need cutoff planes for both legs.
         final SidedPlane testPointCutoffPlane = new SidedPlane(thePoint, testPointXYPlane, testPoint);
         final SidedPlane checkPointCutoffPlane = new SidedPlane(testPoint, travelPlane, thePoint);
@@ -193,9 +210,9 @@ class GeoComplexPolygon extends GeoBasePolygon {
         final SidedPlane checkPointOtherCutoffPlane = new SidedPlane(thePoint, travelPlane, intersectionPoints[0]);
         // Note: we need to handle the cases where end point of the leg sits on an edge!
         // MHL
-        final CrossingEdgeIterator testPointEdgeIterator = new CrossingEdgeIterator(testPointXYPlane, testPointCutoffPlane, testPointOtherCutoffPlane, null);
+        final CrossingEdgeIterator testPointEdgeIterator = new CrossingEdgeIterator(testPointXYPlane, testPointXYAbovePlane, testPointXYBelowPlane, testPointCutoffPlane, testPointOtherCutoffPlane, null);
         zTree.traverse(testPointEdgeIterator, testPoint.z, testPoint.z);
-        final CrossingEdgeIterator checkPointEdgeIterator = new CrossingEdgeIterator(travelPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane, thePoint);
+        final CrossingEdgeIterator checkPointEdgeIterator = new CrossingEdgeIterator(travelPlane, travelAbovePlane, travelBelowPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane, thePoint);
         if (!xTree.traverse(checkPointEdgeIterator, thePoint.x, thePoint.x)) {
           // Endpoint is on edge
           return true;
@@ -205,6 +222,8 @@ class GeoComplexPolygon extends GeoBasePolygon {
         // Travel in Y and Z
         // We'll do this using the testPointXZPlane, and create a travel plane for the right XY plane.
         final Plane travelPlane = new Plane(0.0, 0.0, 1.0, -thePoint.z);
+        final Plane travelAbovePlane = new Plane(travelPlane, true);
+        final Plane travelBelowPlane = new Plane(travelPlane, false);
         // We need cutoff planes for both legs.
         final SidedPlane testPointCutoffPlane = new SidedPlane(thePoint, testPointXZPlane, testPoint);
         final SidedPlane checkPointCutoffPlane = new SidedPlane(testPoint, travelPlane, thePoint);
@@ -216,9 +235,9 @@ class GeoComplexPolygon extends GeoBasePolygon {
         final SidedPlane checkPointOtherCutoffPlane = new SidedPlane(thePoint, travelPlane, intersectionPoints[0]);
         // Note: we need to handle the cases where end point of the first leg sits on an edge!
         // MHL
-        final CrossingEdgeIterator testPointEdgeIterator = new CrossingEdgeIterator(testPointXZPlane, testPointCutoffPlane, testPointOtherCutoffPlane, null);
+        final CrossingEdgeIterator testPointEdgeIterator = new CrossingEdgeIterator(testPointXZPlane, testPointXZAbovePlane, testPointXZBelowPlane, testPointCutoffPlane, testPointOtherCutoffPlane, null);
         yTree.traverse(testPointEdgeIterator, testPoint.y, testPoint.y);
-        final CrossingEdgeIterator checkPointEdgeIterator = new CrossingEdgeIterator(travelPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane, thePoint);
+        final CrossingEdgeIterator checkPointEdgeIterator = new CrossingEdgeIterator(travelPlane, travelAbovePlane, travelBelowPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane, thePoint);
         if (!zTree.traverse(checkPointEdgeIterator, thePoint.z, thePoint.z)) {
           // Endpoint is on edge
           return true;
@@ -589,10 +608,10 @@ class GeoComplexPolygon extends GeoBasePolygon {
     
     public int crossingCount = 0;
     
-    public CrossingEdgeIterator(final Plane plane, final Membership bound1, final Membership bound2, final Vector thePoint) {
+    public CrossingEdgeIterator(final Plane plane, final Plane abovePlane, final Plane belowPlane, final Membership bound1, final Membership bound2, final Vector thePoint) {
       this.plane = plane;
-      this.abovePlane = new Plane(plane, true);
-      this.belowPlane = new Plane(plane, false);
+      this.abovePlane = abovePlane;
+      this.belowPlane = belowPlane;
       this.bound1 = bound1;
       this.bound2 = bound2;
       this.thePoint = thePoint;


[02/26] lucene-solr:master: Add Edge subclass

Posted by kw...@apache.org.
Add Edge subclass


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/c75df440
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/c75df440
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/c75df440

Branch: refs/heads/master
Commit: c75df440d3d4e2f234013bdc8566bb21628c1df9
Parents: a5e24b2
Author: Karl Wright <Da...@gmail.com>
Authored: Sun Apr 24 02:18:23 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Sun Apr 24 02:18:23 2016 -0400

----------------------------------------------------------------------
 .../spatial3d/geom/GeoComplexPolygon.java       | 23 ++++++++++++++++++++
 1 file changed, 23 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c75df440/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
index 7fc53e8..3071fc8 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
@@ -93,6 +93,29 @@ class GeoComplexPolygon extends GeoBasePolygon {
     // MHL
   }
 
+  /**
+   * An instance of this class describes a single edge, and includes what is necessary to reliably determine intersection
+   * in the context of the even/odd algorithm used.
+   */
+  private static class Edge {
+    public final GeoPoint startPoint;
+    public final GeoPoint endPoint;
+    public final SidedPlane startPlane;
+    public final SidedPlane endPlane;
+    public final Plane plane;
+    public final XYZBounds planeBounds;
+    
+    public Edge(final PlanetModel pm, final GeoPoint startPoint, final GeoPoint endPoint) {
+      this.startPoint = startPoint;
+      this.endPoint = endPoint;
+      this.plane = new Plane(startPoint, endPoint);
+      this.startPlane =  new SidedPlane(endPoint, plane, startPoint);
+      this.endPlane = new SidedPlane(startPoint, plane, endPoint);
+      this.planeBounds = new XYZBounds();
+      this.plane.recordBounds(pm, this.planeBounds, this.startPlane, this.endPlane);
+    }
+  }
+  
   @Override
   public boolean equals(Object o) {
     // MHL


[06/26] lucene-solr:master: Complete the logic for following a path, except for the path endpoint on edge condition.

Posted by kw...@apache.org.
Complete the logic for following a path, except for the path endpoint on edge condition.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/d41537d0
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/d41537d0
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/d41537d0

Branch: refs/heads/master
Commit: d41537d0e21b9de48ed11325eb7d59edb1385991
Parents: 282c9a8
Author: Karl Wright <Da...@gmail.com>
Authored: Mon Apr 25 12:16:56 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Mon Apr 25 12:16:56 2016 -0400

----------------------------------------------------------------------
 .../spatial3d/geom/GeoComplexPolygon.java       | 150 ++++++++++++++-----
 1 file changed, 112 insertions(+), 38 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d41537d0/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
index 02ce02c..122a6eb 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
@@ -40,7 +40,11 @@ class GeoComplexPolygon extends GeoBasePolygon {
   private final ZTree ztree = new ZTree();
   
   private final boolean testPointInSet;
-  private final Plane testPointZPlane;
+  
+  private final Plane testPointXZPlane;
+  private final Plane testPointYZPlane;
+  private final Plane testPointXYPlane;
+  
   private final GeoPoint[] edgePoints;
   private final Edge[] shapeStartEdges;
   
@@ -58,11 +62,11 @@ class GeoComplexPolygon extends GeoBasePolygon {
   public GeoComplexPolygon(final PlanetModel planetModel, final List<List<GeoPoint>> pointsList, final GeoPoint testPoint, final boolean testPointInSet) {
     super(planetModel);
     this.testPointInSet = testPointInSet;
-    Plane p = Plane.constructNormalizedZPlane(testPoint.x, testPoint.y);
-    if (p == null) {
-      p = new Plane(1.0, 0.0, 0.0, 0.0);
-    }
-    this.testPointZPlane = p;
+    
+    this.testPointXZPlane = new Plane(0.0, 1.0, 0.0, -testPoint.y);
+    this.testPointYZPlane = new Plane(1.0, 0.0, 0.0, -testPoint.x);
+    this.testPointXYPlane = new Plane(0.0, 0.0, 1.0, -testPoint.z);
+    
     this.edgePoints = new GeoPoint[pointsList.size()];
     this.shapeStartEdges = new Edge[pointsList.size()];
     int edgePointIndex = 0;
@@ -114,42 +118,112 @@ class GeoComplexPolygon extends GeoBasePolygon {
   
   @Override
   public boolean isWithin(final Vector thePoint) {
-    // Construct a horizontal plane that goes through the provided z value.  This, along with the
-    // testPointZPlane, will provide a way of counting intersections between this point and the test point.
-    // We use z exclusively for this logic at the moment but we may in the future choose x or y based on which
-    // is bigger.
+    // If we're right on top of the point, we know the answer.
     if (testPoint.isNumericallyIdentical(thePoint)) {
-      return true;
+      return testPointInSet;
     }
-    if (testPointZPlane.evaluateIsZero(thePoint)) {
-      // The point we are assessing goes through only one plane.
-      // Compute cutoff planes
-      final SidedPlane testPointCutoff =  new SidedPlane(thePoint, testPointZPlane, testPoint);
-      final SidedPlane checkPointCutoff = new SidedPlane(testPoint, testPointZPlane, thePoint);
-
-      // Count crossings
-      final CrossingEdgeIterator crossingEdgeIterator = new CrossingEdgeIterator(testPointZPlane, testPointCutoff, checkPointCutoff);
-      
-      // Compute bounds for this part of testZPlane
-      final XYZBounds testZPlaneBounds = new XYZBounds();
-      testPointZPlane.recordBounds(planetModel, testZPlaneBounds, testPointCutoff, checkPointCutoff);
+    
+    // Choose our navigation route!
+    final double xDelta = Math.abs(thePoint.x - testPoint.x);
+    final double yDelta = Math.abs(thePoint.y - testPoint.y);
+    final double zDelta = Math.abs(thePoint.z - testPoint.z);
+    
+    // If we're right on top of any of the test planes, we navigate solely on that plane.
+    if (testPointXZPlane.evaluateIsZero(thePoint)) {
+      // Use the XZ plane exclusively.
+      final SidedPlane testPointCutoff =  new SidedPlane(thePoint, testPointXZPlane, testPoint);
+      final SidedPlane checkPointCutoff = new SidedPlane(testPoint, testPointXZPlane, thePoint);
+      // Note: need to detect condition where edge endpoint is the check point!
+      // MHL
+      final CrossingEdgeIterator crossingEdgeIterator = new CrossingEdgeIterator(testPointXZPlane, testPointCutoff, checkPointCutoff);
+      // Traverse our way from the test point to the check point.  Use the y tree because that's fixed.
+      yTree.traverse(crossingEdgeIterator, testPoint.y, testPoint.y);
+      return ((crossingEdgeIterator.crossingCount & 1) == 0)?testPointInSet:!testPointInSet;
+    } else if (testPointYZPlane.evaluateIsZero(thePoint)) {
+      // Use the YZ plane exclusively.
+      final SidedPlane testPointCutoff =  new SidedPlane(thePoint, testPointYZPlane, testPoint);
+      final SidedPlane checkPointCutoff = new SidedPlane(testPoint, testPointYZPlane, thePoint);
+      // Note: need to detect condition where edge endpoint is the check point!
+      // MHL
+      final CrossingEdgeIterator crossingEdgeIterator = new CrossingEdgeIterator(testPointYZPlane, testPointCutoff, checkPointCutoff);
+      // Traverse our way from the test point to the check point.  Use the x tree because that's fixed.
+      xTree.traverse(crossingEdgeIterator, testPoint.x, testPoint.x);
+      return ((crossingEdgeIterator.crossingCount & 1) == 0)?testPointInSet:!testPointInSet;
+    } else if (testPointXYPlane.evaluateIsZero(thePoint)) {
+      // Use the XY plane exclusively.
+      final SidedPlane testPointCutoff =  new SidedPlane(thePoint, testPointXYPlane, testPoint);
+      final SidedPlane checkPointCutoff = new SidedPlane(testPoint, testPointXYPlane, thePoint);
+      // Note: need to detect condition where edge endpoint is the check point!
+      // MHL
+      final CrossingEdgeIterator crossingEdgeIterator = new CrossingEdgeIterator(testPointXYPlane, testPointCutoff, checkPointCutoff);
+      // Traverse our way from the test point to the check point.  Use the z tree because that's fixed.
+      zTree.traverse(crossingEdgeIterator, testPoint.z, testPoint.z);
+      return ((crossingEdgeIterator.crossingCount & 1) == 0)?testPointInSet:!testPointInSet;
+    } else {
       
-      // Pick which tree to use
-      final double xDelta = testZPlaneBounds.getMaximumX() - testZPlaneBounds.getMinimumX();
-      final double yDelta = testZPlaneBounds.getMaximumY() - testZPlaneBounds.getMinimumY();
-      if (xDelta <= yDelta) {
-        xTree.traverse(crossingEdgeIterator, testZPlaneBounds.getMinimumX(), testZPlaneBounds.getMaximumX());
-      } else if (yDelta <= xDelta) {
-        yTree.traverse(crossingEdgeIterator, testZPlaneBounds.getMinimumY(), testZPlaneBounds.getMaximumY());
+      // We need to use two planes to get there.  We can use any two planes, and order doesn't matter.
+      // The best to pick are the ones with the shortest overall distance.
+      if (xDelta + yDelta <= xDelta + zDelta && xDelta + yDelta <= yDelta + zDelta) {
+        // Travel in X and Y
+        // We'll do this using the testPointYZPlane, and create a travel plane for the right XZ plane.
+        final Plane travelPlane = new Plane(0.0, 1.0, 0.0, -thePoint.y);
+        // We need cutoff planes for both legs.
+        final SidedPlane testPointCutoffPlane = new SidedPlane(thePoint, testPointYZPlane, testPoint);
+        final SidedPlane checkPointCutoffPlane = new SidedPlane(testPoint, travelPlane, thePoint);
+        // Now, find the intersection of the check and test point planes.
+        final GeoPoint[] intersectionPoints = travelPlane.findIntersections(planetModel, testPointYZPlane, testPointCutoffPlane, checkPointCutoffPlane);
+        assert intersectionPoints != null : "couldn't find any intersections";
+        assert intersectionPoints.length != 1 : "wrong number of intersection points";
+        final SidedPlane testPointOtherCutoffPlane = new SidedPlane(testPoint, testPointYZPlane, intersectionPoints[0]);
+        final SidedPlane checkPointOtherCutoffPlane = new SidedPlane(thePoint, travelPlane, intersectionPoints[0]);
+        // Note: we need to handle the cases where end point of the leg sits on an edge!
+        // MHL
+        final CrossingEdgeIterator testPointEdgeIterator = new CrossingEdgeIterator(testPointYZPlane, testPointCutoffPlane, testPointOtherCutoffPlane);
+        xTree.traverse(testPointEdgeIterator, testPoint.x, testPoint.x);
+        final CrossingEdgeIterator checkPointEdgeIterator = new CrossingEdgeIterator(travelPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane);
+        yTree.traverse(checkPointEdgeIterator, thePoint.y, thePoint.y);
+        return (((testPointEdgeIterator.crossingCount + checkPointEdgeIterator.crossingCount) & 1) == 0)?testPointInSet:!testPointInSet;
+      } else if (xDelta + zDelta <= xDelta + yDelta && xDelta + zDelta <= zDelta + yDelta) {
+        // Travel in X and Z
+        // We'll do this using the testPointXYPlane, and create a travel plane for the right YZ plane.
+        final Plane travelPlane = new Plane(1.0, 0.0, 0.0, -thePoint.x);
+        // We need cutoff planes for both legs.
+        final SidedPlane testPointCutoffPlane = new SidedPlane(thePoint, testPointXYPlane, testPoint);
+        final SidedPlane checkPointCutoffPlane = new SidedPlane(testPoint, travelPlane, thePoint);
+        // Now, find the intersection of the check and test point planes.
+        final GeoPoint[] intersectionPoints = travelPlane.findIntersections(planetModel, testPointXYPlane, testPointCutoffPlane, checkPointCutoffPlane);
+        assert intersectionPoints != null : "couldn't find any intersections";
+        assert intersectionPoints.length != 1 : "wrong number of intersection points";
+        final SidedPlane testPointOtherCutoffPlane = new SidedPlane(testPoint, testPointXYPlane, intersectionPoints[0]);
+        final SidedPlane checkPointOtherCutoffPlane = new SidedPlane(thePoint, travelPlane, intersectionPoints[0]);
+        // Note: we need to handle the cases where end point of the leg sits on an edge!
+        // MHL
+        final CrossingEdgeIterator testPointEdgeIterator = new CrossingEdgeIterator(testPointXYPlane, testPointCutoffPlane, testPointOtherCutoffPlane);
+        zTree.traverse(testPointEdgeIterator, testPoint.z, testPoint.z);
+        final CrossingEdgeIterator checkPointEdgeIterator = new CrossingEdgeIterator(travelPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane);
+        xTree.traverse(checkPointEdgeIterator, thePoint.x, thePoint.x);
+        return (((testPointEdgeIterator.crossingCount + checkPointEdgeIterator.crossingCount) & 1) == 0)?testPointInSet:!testPointInSet;
+      } else if (yDelta + zDelta <= xDelta + yDelta && yDelta + zDelta <= xDelta + zDelta) {
+        // Travel in Y and Z
+        // We'll do this using the testPointXZPlane, and create a travel plane for the right XY plane.
+        final Plane travelPlane = new Plane(0.0, 0.0, 1.0, -thePoint.z);
+        // We need cutoff planes for both legs.
+        final SidedPlane testPointCutoffPlane = new SidedPlane(thePoint, testPointXZPlane, testPoint);
+        final SidedPlane checkPointCutoffPlane = new SidedPlane(testPoint, travelPlane, thePoint);
+        // Now, find the intersection of the check and test point planes.
+        final GeoPoint[] intersectionPoints = travelPlane.findIntersections(planetModel, testPointXZPlane, testPointCutoffPlane, checkPointCutoffPlane);
+        assert intersectionPoints != null : "couldn't find any intersections";
+        assert intersectionPoints.length != 1 : "wrong number of intersection points";
+        final SidedPlane testPointOtherCutoffPlane = new SidedPlane(testPoint, testPointXZPlane, intersectionPoints[0]);
+        final SidedPlane checkPointOtherCutoffPlane = new SidedPlane(thePoint, travelPlane, intersectionPoints[0]);
+        // Note: we need to handle the cases where end point of the leg sits on an edge!
+        // MHL
+        final CrossingEdgeIterator testPointEdgeIterator = new CrossingEdgeIterator(testPointXZPlane, testPointCutoffPlane, testPointOtherCutoffPlane);
+        yTree.traverse(testPointEdgeIterator, testPoint.y, testPoint.y);
+        final CrossingEdgeIterator checkPointEdgeIterator = new CrossingEdgeIterator(travelPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane);
+        zTree.traverse(checkPointEdgeIterator, thePoint.z, thePoint.z);
+        return (((testPointEdgeIterator.crossingCount + checkPointEdgeIterator.crossingCount) & 1) == 0)?testPointInSet:!testPointInSet;
       }
-      return ((crossingEdgeIterator.getCrossingCount() & 0x00000001) == 0)?testPointInSet:!testPointInSet;
-    } else {
-      // The point requires two planes
-      final Plane xyPlane = new Plane(planetModel, z);
-      // We need to plan a path from the test point to the point to be evaluated.
-      // This requires finding the intersection point between the xyPlane and the testPointZPlane
-      // that is closest to our point.
-      // MHL
     }
     return false;
   }


[03/26] lucene-solr:master: More work on GeoComplexPolygon

Posted by kw...@apache.org.
More work on GeoComplexPolygon


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/b9c2bf7d
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/b9c2bf7d
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/b9c2bf7d

Branch: refs/heads/master
Commit: b9c2bf7d282abdce849f3165f263ab2df98e0377
Parents: c75df44
Author: Karl Wright <Da...@gmail.com>
Authored: Sun Apr 24 17:44:40 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Sun Apr 24 17:44:40 2016 -0400

----------------------------------------------------------------------
 .../spatial3d/geom/GeoComplexPolygon.java       | 305 ++++++++++++++++++-
 1 file changed, 303 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b9c2bf7d/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
index 3071fc8..df89f55 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
@@ -35,6 +35,15 @@ import java.util.Map;
  */
 class GeoComplexPolygon extends GeoBasePolygon {
   
+  private final XTree xtree = new XTree();
+  private final YTree ytree = new YTree();
+  private final ZTree ztree = new ZTree();
+  
+  private final boolean testPointInSet;
+  private final Plane testPointVerticalPlane;
+  private final GeoPoint[] edgePoints;
+  private final Edge[] shapeStartEdges;
+  
   /**
    * Create a complex polygon from multiple lists of points, and a single point which is known to be in or out of
    * set.
@@ -48,7 +57,41 @@ class GeoComplexPolygon extends GeoBasePolygon {
    */
   public GeoComplexPolygon(final PlanetModel planetModel, final List<List<GeoPoint>> pointsList, final GeoPoint testPoint, final boolean testPointInSet) {
     super(planetModel);
-    // MHL
+    this.testPointInSet = testPointInSet;
+    Plane p = Plane.constructNormalizedZPlane(testPoint.x, testPoint.y);
+    if (p == null) {
+      p = new Plane(1.0, 0.0, 0.0, 0.0);
+    }
+    this.testPointVerticalPlane = p;
+    this.edgePoints = new GeoPoint[pointsList.size()];
+    this.shapeStartEdges = new Edge[pointsList.size()];
+    int edgePointIndex = 0;
+    for (final List<GeoPoint> shapePoints : pointsList) {
+      GeoPoint lastGeoPoint = pointsList.get(shapePoints.size()-1);
+      edgePoints[edgePointIndex] = lastGeoPoint;
+      Edge lastEdge = null;
+      Edge firstEdge = null;
+      for (final GeoPoint thisGeoPoint : shapePoints) {
+        final Edge edge = new Edge(planetModel, lastGeoPoint, thisGeoPoint);
+        xtree.add(edge);
+        ytree.add(edge);
+        ztree.add(edge);
+        // Now, link
+        if (firstEdge == null) {
+          firstEdge = edge;
+        }
+        if (lastEdge != null) {
+          lastEdge.next = edge;
+          edge.previous = lastEdge;
+        }
+        lastEdge = edge;
+        lastGeoPoint = thisGeoPoint;
+      }
+      firstEdge.previous = lastEdge;
+      lastEdge.next = firstEdge;
+      shapeStartEdges[edgePointIndex] = firstEdge;
+      edgePointIndex++;
+    }
   }
 
   /** Compute a legal point index from a possibly illegal one, that may have wrapped.
@@ -85,12 +128,22 @@ class GeoComplexPolygon extends GeoBasePolygon {
   @Override
   public void getBounds(Bounds bounds) {
     super.getBounds(bounds);
-    // MHL
+    for (final Edge startEdge : shapeStartEdges) {
+      Edge currentEdge = startEdge;
+      while (true) {
+        currentEdge.plane.recordBounds(this.planetModel, currentEdge.startPlane, currentEdge.edgePlane);
+        currentEdge = currentEdge.next;
+        if (currentEdge == startEdge) {
+          break;
+        }
+      }
+    }
   }
 
   @Override
   protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
     // MHL
+    return 0.0;
   }
 
   /**
@@ -104,6 +157,8 @@ class GeoComplexPolygon extends GeoBasePolygon {
     public final SidedPlane endPlane;
     public final Plane plane;
     public final XYZBounds planeBounds;
+    public Edge previous = null;
+    public Edge next = null;
     
     public Edge(final PlanetModel pm, final GeoPoint startPoint, final GeoPoint endPoint) {
       this.startPoint = startPoint;
@@ -116,6 +171,252 @@ class GeoComplexPolygon extends GeoBasePolygon {
     }
   }
   
+  /**
+   * Iterator execution interface, for tree traversal.  Pass an object implementing this interface
+   * into the traversal method of a tree, and each edge that matches will cause this object to be
+   * called.
+   */
+  private static interface EdgeIterator {
+    /**
+     * @param edge is the edge that matched.
+     * @return true if the iteration should continue, false otherwise.
+     */
+    public boolean matches(final Edge edge);
+  }
+  
+  /**
+   * Comparison interface for tree traversal.  An object implementing this interface
+   * gets to decide the relationship between the Edge object and the criteria being considered.
+   */
+  private static interface TraverseComparator {
+    
+    /**
+     * Compare an edge.
+     * @param edge is the edge to compare.
+     * @param value is the value to compare.
+     * @return -1 if "less" than this one, 0 if overlaps, or 1 if "greater".
+     */
+    public int compare(final Edge edge, final double value);
+    
+  }
+
+  /**
+   * Comparison interface for tree addition.  An object implementing this interface
+   * gets to decide the relationship between the Edge object and the criteria being considered.
+   */
+  private static interface AddComparator {
+    
+    /**
+     * Compare an edge.
+     * @param edge is the edge to compare.
+     * @param addEdge is the edge being added.
+     * @return -1 if "less" than this one, 0 if overlaps, or 1 if "greater".
+     */
+    public int compare(final Edge edge, final Edge addEdge);
+    
+  }
+  
+  /**
+   * An instance of this class represents a node in a tree.  The tree is designed to be given
+   * a value and from that to iterate over a list of edges.
+   * In order to do this efficiently, each new edge is dropped into the tree using its minimum and
+   * maximum value.  If the new edge's value does not overlap the range, then it gets added
+   * either to the lesser side or the greater side, accordingly.  If it does overlap, then the
+   * "overlapping" chain is instead traversed.
+   *
+   * This class is generic and can be used for any definition of "value".
+   *
+   */
+  private static class Node {
+    public final Edge edge;
+    public Node lesser = null;
+    public Node greater = null;
+    public Node overlaps = null;
+    
+    public Node(final Edge edge) {
+      this.edge = edge;
+    }
+    
+    public void add(final Edge newEdge, final AddComparator edgeComparator) {
+      Node currentNode = this;
+      while (true) {
+        final int result = edgeComparator.compare(edge, newEdge);
+        if (result < 0) {
+          if (lesser == null) {
+            lesser = new Node(newEdge);
+            return;
+          }
+          currentNode = lesser;
+        } else if (result > 0) {
+          if (greater == null) {
+            greater = new Node(newEdge);
+            return;
+          }
+          currentNode = greater;
+        } else {
+          if (overlaps == null) {
+            overlaps = new Node(newEdge);
+            return;
+          }
+          currentNode = overlaps;
+        }
+      }
+    }
+    
+    public boolean traverse(final EdgeIterator edgeIterator, final TraverseComparator edgeComparator, final double value) {
+      Node currentNode = this;
+      while (currentNode != null) {
+        final int result = edgeComparator.compare(currentNode.edge, value);
+        if (result < 0) {
+          currentNode = lesser;
+        } else if (result > 0) {
+          currentNode = greater;
+        } else {
+          if (!edgeIterator.matches(edge)) {
+            return false;
+          }
+          currentNode = overlaps;
+        }
+      }
+      return true;
+    }
+  }
+  
+  /** This is the z-tree.
+   */
+  private static class ZTree implements TraverseComparator, AddComparator {
+    public Node rootNode = null;
+    
+    public ZTree() {
+    }
+    
+    public void add(final Edge edge) {
+      if (rootNode == null) {
+        rootNode = new Node(edge);
+      } else {
+        rootNode.add(edge, this);
+      }
+    }
+    
+    public boolean traverse(final EdgeIterator edgeIterator, final double value) {
+      if (rootNode == null) {
+        return true;
+      }
+      return rootNode.traverse(edgeIterator, this, value);
+    }
+    
+    @Override
+    public int compare(final Edge edge, final Edge addEdge) {
+      if (edge.planeBounds.getMaximumZ() < addEdge.planeBounds.getMinimumZ()) {
+        return 1;
+      } else if (edge.planeBounds.getMinimumZ() > addEdge.planeBounds.getMaximumZ()) {
+        return -1;
+      }
+      return 0;
+    }
+    
+    @Override
+    public int compare(final Edge edge, final double value) {
+      if (edge.planeBounds.getMinimumZ() > value) {
+        return -1;
+      } else if (edge.planeBounds.getMaximumZ() < value) {
+        return 1;
+      }
+      return 0;
+    }
+    
+  }
+  
+  /** This is the y-tree.
+   */
+  private static class YTree implements TraverseComparator, AddComparator {
+    public Node rootNode = null;
+    
+    public YTree() {
+    }
+    
+    public void add(final Edge edge) {
+      if (rootNode == null) {
+        rootNode = new Node(edge);
+      } else {
+        rootNode.add(edge, this);
+      }
+    }
+    
+    public boolean traverse(final EdgeIterator edgeIterator, final double value) {
+      if (rootNode == null) {
+        return true;
+      }
+      return rootNode.traverse(edgeIterator, this, value);
+    }
+    
+    @Override
+    public int compare(final Edge edge, final Edge addEdge) {
+      if (edge.planeBounds.getMaximumY() < addEdge.planeBounds.getMinimumY()) {
+        return 1;
+      } else if (edge.planeBounds.getMinimumY() > addEdge.planeBounds.getMaximumY()) {
+        return -1;
+      }
+      return 0;
+    }
+    
+    @Override
+    public int compare(final Edge edge, final double value) {
+      if (edge.planeBounds.getMinimumY() > value) {
+        return -1;
+      } else if (edge.planeBounds.getMaximumY() < value) {
+        return 1;
+      }
+      return 0;
+    }
+    
+  }
+
+  /** This is the x-tree.
+   */
+  private static class XTree implements TraverseComparator, AddComparator {
+    public Node rootNode = null;
+    
+    public XTree() {
+    }
+    
+    public void add(final Edge edge) {
+      if (rootNode == null) {
+        rootNode = new Node(edge);
+      } else {
+        rootNode.add(edge, this);
+      }
+    }
+    
+    public boolean traverse(final EdgeIterator edgeIterator, final double value) {
+      if (rootNode == null) {
+        return true;
+      }
+      return rootNode.traverse(edgeIterator, this, value);
+    }
+    
+    @Override
+    public int compare(final Edge edge, final Edge addEdge) {
+      if (edge.planeBounds.getMaximumX() < addEdge.planeBounds.getMinimumX()) {
+        return 1;
+      } else if (edge.planeBounds.getMinimumX() > addEdge.planeBounds.getMaximumX()) {
+        return -1;
+      }
+      return 0;
+    }
+    
+    @Override
+    public int compare(final Edge edge, final double value) {
+      if (edge.planeBounds.getMinimumX() > value) {
+        return -1;
+      } else if (edge.planeBounds.getMaximumX() < value) {
+        return 1;
+      }
+      return 0;
+    }
+    
+  }
+
   @Override
   public boolean equals(Object o) {
     // MHL


[12/26] lucene-solr:master: Separate point filtering from edge filtering.

Posted by kw...@apache.org.
Separate point filtering from edge filtering.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/bb7d4585
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/bb7d4585
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/bb7d4585

Branch: refs/heads/master
Commit: bb7d4585c49b9ca96b2ec10d593772461d3e666a
Parents: f3748eb
Author: Karl Wright <Da...@gmail.com>
Authored: Tue Apr 26 08:03:02 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Tue Apr 26 08:03:02 2016 -0400

----------------------------------------------------------------------
 .../lucene/spatial3d/geom/GeoPolygonFactory.java   | 17 +++++++++++++----
 .../lucene/spatial3d/geom/GeoPolygonTest.java      |  8 ++++----
 2 files changed, 17 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bb7d4585/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java
index 99fc7c9..436b5ac 100755
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java
@@ -88,7 +88,7 @@ public class GeoPolygonFactory {
     // First, exercise a sanity filter on the provided pointList, and remove identical points, linear points, and backtracks
     //System.err.println(" filtering "+pointList.size()+" points...");
     //final long startTime = System.currentTimeMillis();
-    final List<GeoPoint> filteredPointList = filterPoints(pointList, leniencyValue);
+    final List<GeoPoint> filteredPointList = filterEdges(filterPoints(pointList), leniencyValue);
     //System.err.println("  ...done in "+(System.currentTimeMillis()-startTime)+"ms ("+((filteredPointList==null)?"degenerate":(filteredPointList.size()+" points"))+")");
     if (filteredPointList == null) {
       return null;
@@ -165,12 +165,11 @@ public class GeoPolygonFactory {
     }
   }
 
-  /** Filter duplicate points and coplanar points.
+  /** Filter duplicate points.
    * @param input with input list of points
-   * @param leniencyValue is the allowed distance of a point from the plane for cleanup of overly detailed polygons
    * @return the filtered list, or null if we can't get a legit polygon from the input.
    */
-  static List<GeoPoint> filterPoints(final List<GeoPoint> input, final double leniencyValue) {
+  static List<GeoPoint> filterPoints(final List<GeoPoint> input) {
     
     final List<GeoPoint> noIdenticalPoints = new ArrayList<>(input.size());
     
@@ -212,6 +211,16 @@ public class GeoPolygonFactory {
       return null;
     }
     
+    return noIdenticalPoints;
+  }
+  
+  /** Filter coplanar points.
+   * @param noIdenticalPoints with input list of points
+   * @param leniencyValue is the allowed distance of a point from the plane for cleanup of overly detailed polygons
+   * @return the filtered list, or null if we can't get a legit polygon from the input.
+   */
+  static List<GeoPoint> filterEdges(final List<GeoPoint> noIdenticalPoints, final double leniencyValue) {
+  
     // Now, do the depth-first search needed to find a path that has no coplanarities in it.
     // This is, unfortunately, not easy, because coplanarity is not transitive as you walk around the polygon.
     // If point C is not coplanar with edge A-B, there is no guarantee that A is not coplanar with B-C.

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bb7d4585/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
index ce43c5b..d76ae4e 100755
--- a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
+++ b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
@@ -44,7 +44,7 @@ public class GeoPolygonTest {
       originalPoints.add(point2);
       originalPoints.add(point2);
       originalPoints.add(point3);
-      final List<GeoPoint> filteredPoints =GeoPolygonFactory.filterPoints(originalPoints, 0.0);
+      final List<GeoPoint> filteredPoints = GeoPolygonFactory.filterEdges(GeoPolygonFactory.filterPoints(originalPoints), 0.0);
       assertEquals(3, filteredPoints.size());
       assertEquals(point1, filteredPoints.get(0));
       assertEquals(point2, filteredPoints.get(1));
@@ -57,7 +57,7 @@ public class GeoPolygonTest {
       originalPoints.add(point1);
       originalPoints.add(point3);
       originalPoints.add(point2);
-      final List<GeoPoint> filteredPoints =GeoPolygonFactory.filterPoints(originalPoints, 0.0);
+      final List<GeoPoint> filteredPoints = GeoPolygonFactory.filterEdges(GeoPolygonFactory.filterPoints(originalPoints), 0.0);
       assertEquals(3, filteredPoints.size());
       assertEquals(point2, filteredPoints.get(0));
       assertEquals(point1, filteredPoints.get(1));
@@ -71,7 +71,7 @@ public class GeoPolygonTest {
       originalPoints.add(point3);
       originalPoints.add(point4);
       originalPoints.add(point5);
-      final List<GeoPoint> filteredPoints =GeoPolygonFactory.filterPoints(originalPoints, 0.0);
+      final List<GeoPoint> filteredPoints = GeoPolygonFactory.filterEdges(GeoPolygonFactory.filterPoints(originalPoints), 0.0);
       assertEquals(3, filteredPoints.size());
       assertEquals(point1, filteredPoints.get(0));
       assertEquals(point3, filteredPoints.get(1));
@@ -85,7 +85,7 @@ public class GeoPolygonTest {
       originalPoints.add(point3);
       originalPoints.add(point4);
       System.err.println("Before: "+originalPoints);
-      final List<GeoPoint> filteredPoints =GeoPolygonFactory.filterPoints(originalPoints, 0.0);
+      final List<GeoPoint> filteredPoints = GeoPolygonFactory.filterEdges(GeoPolygonFactory.filterPoints(originalPoints), 0.0);
       System.err.println("After: "+filteredPoints);
       assertEquals(3, filteredPoints.size());
       assertEquals(point5, filteredPoints.get(0));


[19/26] lucene-solr:master: Use more accurate version of inside cutoff planes.

Posted by kw...@apache.org.
Use more accurate version of inside cutoff planes.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/86be6a67
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/86be6a67
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/86be6a67

Branch: refs/heads/master
Commit: 86be6a67db81b74a57ebb188a761c96095dc37b6
Parents: f896afc
Author: Karl Wright <Da...@gmail.com>
Authored: Wed Apr 27 20:09:06 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Wed Apr 27 20:09:06 2016 -0400

----------------------------------------------------------------------
 .../spatial3d/geom/GeoComplexPolygon.java       | 14 ++++---
 .../lucene/spatial3d/geom/GeoPolygonTest.java   | 44 ++++++++++++++++++++
 2 files changed, 52 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/86be6a67/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
index 3a89ee5..06d6dba 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
@@ -865,8 +865,10 @@ class GeoComplexPolygon extends GeoBasePolygon {
       this.testPointCutoffPlane = new SidedPlane(thePoint, testPointPlane, testPoint);
       this.checkPointCutoffPlane = new SidedPlane(testPoint, travelPlane, thePoint);
 
-      this.testPointOtherCutoffPlane = new SidedPlane(testPoint, travelPlane, -(travelPlane.x * intersectionPoint.x + travelPlane.y * intersectionPoint.y + travelPlane.z * intersectionPoint.z));
-      this.checkPointOtherCutoffPlane = new SidedPlane(thePoint, testPointPlane, -(testPointPlane.x * intersectionPoint.x + testPointPlane.y * intersectionPoint.y + testPointPlane.z * intersectionPoint.z));
+      // Convert travel plane to a sided plane
+      this.testPointOtherCutoffPlane = new SidedPlane(testPoint, travelPlane, travelPlane.D);
+      // Convert testPoint plane to a sided plane
+      this.checkPointOtherCutoffPlane = new SidedPlane(thePoint, testPointPlane, testPointPlane.D);
         
       // Figure out which of the above/below planes are inside vs. outside.  To do this,
       // we look for the point that is within the bounds of the testPointPlane and travelPlane.  The two sides that intersected there are the inside
@@ -881,8 +883,8 @@ class GeoComplexPolygon extends GeoBasePolygon {
       assert belowBelow != null : "Below + below should not be coplanar";
       final GeoPoint[] belowAbove = travelBelowPlane.findIntersections(planetModel, testPointAbovePlane, testPointCutoffPlane, testPointOtherCutoffPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane);
       assert belowAbove != null : "Below + above should not be coplanar";
-      
-      assert aboveAbove.length + aboveBelow.length + belowBelow.length + belowAbove.length == 1 : "Can be exactly one inside point, instead was: aa="+aboveAbove.length+" ab=" + aboveBelow.length+" bb="+ belowBelow.length+" ba=" + belowAbove.length;
+
+      assert ((aboveAbove.length > 0)?1:0) + ((aboveBelow.length > 0)?1:0) + ((belowBelow.length > 0)?1:0) + ((belowAbove.length > 0)?1:0) == 1 : "Can be exactly one inside point, instead was: aa="+aboveAbove.length+" ab=" + aboveBelow.length+" bb="+ belowBelow.length+" ba=" + belowAbove.length;
       
       final GeoPoint insideIntersection;
       if (aboveAbove.length > 0) {
@@ -911,8 +913,8 @@ class GeoComplexPolygon extends GeoBasePolygon {
         insideIntersection = belowAbove[0];
       }
       
-      insideTravelCutoffPlane = new SidedPlane(thePoint, travelInsidePlane, insideIntersection);
-      insideTestPointCutoffPlane = new SidedPlane(testPoint, testPointInsidePlane, insideIntersection);
+      insideTravelCutoffPlane = new SidedPlane(thePoint, testPointInsidePlane, testPointInsidePlane.D);
+      insideTestPointCutoffPlane = new SidedPlane(testPoint, travelInsidePlane, travelInsidePlane.D);
 
     }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/86be6a67/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
index a196495..7a152b9 100755
--- a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
+++ b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
@@ -134,6 +134,12 @@ public class GeoPolygonTest {
     gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.5);
     assertTrue(c.isWithin(gp));
 
+    shapes = new ArrayList<>();
+    shapes.add(new GeoPolygonFactory.PolygonDescription(points));
+    
+    c = GeoPolygonFactory.makeLargeGeoPolygon(PlanetModel.SPHERE, shapes);
+    assertTrue(c.isWithin(gp));
+
   }
 
   @Test
@@ -141,6 +147,7 @@ public class GeoPolygonTest {
     GeoPolygon c;
     GeoPoint gp;
     List<GeoPoint> points;
+    List<GeoPolygonFactory.PolygonDescription> shapes;
 
     points = new ArrayList<GeoPoint>();
     points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.4));
@@ -177,6 +184,43 @@ public class GeoPolygonTest {
     gp = new GeoPoint(PlanetModel.SPHERE, 0.0, Math.PI);
     assertFalse(c.isWithin(gp));
 
+    // Now, same thing for large polygon
+    shapes = new ArrayList<>();
+    shapes.add(new GeoPolygonFactory.PolygonDescription(points));
+    
+    c = GeoPolygonFactory.makeLargeGeoPolygon(PlanetModel.SPHERE, shapes);
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.65);
+    assertFalse(c.isWithin(gp)); //??? fails
+
+    // Sample some points within
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.45);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.5);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.55);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.05, -0.5);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.05, -0.5);
+    assertTrue(c.isWithin(gp));
+    // Sample some nearby points outside
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.65);
+    assertFalse(c.isWithin(gp)); //??? fails
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.35);
+    assertFalse(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.15, -0.5);
+    assertFalse(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.15, -0.5);
+    assertFalse(c.isWithin(gp));
+    // Random points outside
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0);
+    assertFalse(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, Math.PI * 0.5, 0.0);
+    assertFalse(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, Math.PI);
+    assertFalse(c.isWithin(gp));
+
+    // Next bunch of small polygon points
     points = new ArrayList<GeoPoint>();
     points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.4));
     points.add(new GeoPoint(PlanetModel.SPHERE, 0.1, -0.5));


[16/26] lucene-solr:master: Flesh out remaining methods

Posted by kw...@apache.org.
Flesh out remaining methods


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/be0fff05
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/be0fff05
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/be0fff05

Branch: refs/heads/master
Commit: be0fff05bcdda275aa034d208e8969e9e0125a4a
Parents: 7a4c077
Author: Karl Wright <Da...@gmail.com>
Authored: Wed Apr 27 08:26:03 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Wed Apr 27 08:26:03 2016 -0400

----------------------------------------------------------------------
 .../spatial3d/geom/GeoComplexPolygon.java       | 31 +++++++++++++++-----
 1 file changed, 24 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/be0fff05/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
index 9b0fd1f..47ca961 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
@@ -254,8 +254,25 @@ class GeoComplexPolygon extends GeoBasePolygon {
 
   @Override
   protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
-    // MHL
-    return 0.0;
+    double minimumDistance = Double.MAX_VALUE;
+    for (final Edge shapeStartEdge : shapeStartEdges) {
+      Edge shapeEdge = shapeStartEdge;
+      while (true) {
+        final double newDist = distanceStyle.computeDistance(shapeEdge.startPoint, x, y, z);
+        if (newDist < minimumDistance) {
+          minimumDistance = newDist;
+        }
+        final double newPlaneDist = distanceStyle.computeDistance(planetModel, shapeEdge.plane, x, y, z, shapeEdge.startPlane, shapeEdge.endPlane);
+        if (newPlaneDist < minimumDistance) {
+          minimumDistance = newPlaneDist;
+        }
+        shapeEdge = shapeEdge.next;
+        if (shapeEdge == shapeStartEdge) {
+          break;
+        }
+      }
+    }
+    return minimumDistance;
   }
 
   /**
@@ -1002,19 +1019,19 @@ class GeoComplexPolygon extends GeoBasePolygon {
   
   @Override
   public boolean equals(Object o) {
-    // MHL
-    return false;
+    // Way too expensive to do this the hard way, so each complex polygon will be considered unique.
+    return this == o;
   }
 
   @Override
   public int hashCode() {
-    // MHL
-    return 0;
+    // Each complex polygon is considered unique.
+    return System.identityHashCode(this);
   }
 
   @Override
   public String toString() {
-    return "GeoComplexPolygon: {planetmodel=" + planetModel + "}";
+    return "GeoComplexPolygon: {planetmodel=" + planetModel + ", number of shapes="+shapeStartEdges.length+", address="+ Integer.toHexString(hashCode())+"}";
   }
 }
   


[23/26] lucene-solr:master: Finish debugging simple case

Posted by kw...@apache.org.
Finish debugging simple case


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/da896f80
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/da896f80
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/da896f80

Branch: refs/heads/master
Commit: da896f803dcaf2eb5dc1753eda05c7499b07e0c6
Parents: 3d7fd5e
Author: Karl Wright <Da...@gmail.com>
Authored: Thu Apr 28 19:20:03 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Thu Apr 28 19:20:03 2016 -0400

----------------------------------------------------------------------
 .../spatial3d/geom/GeoComplexPolygon.java       | 217 ++++++++++---------
 .../lucene/spatial3d/geom/GeoPolygonTest.java   |   3 -
 2 files changed, 117 insertions(+), 103 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/da896f80/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
index c218776..661953a 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
@@ -40,15 +40,15 @@ class GeoComplexPolygon extends GeoBasePolygon {
   private final boolean testPointInSet;
   private final GeoPoint testPoint;
   
-  private final Plane testPointXZPlane;
-  private final Plane testPointXZAbovePlane;
-  private final Plane testPointXZBelowPlane;
-  private final Plane testPointYZPlane;
-  private final Plane testPointYZAbovePlane;
-  private final Plane testPointYZBelowPlane;
-  private final Plane testPointXYPlane;
-  private final Plane testPointXYAbovePlane;
-  private final Plane testPointXYBelowPlane;
+  private final Plane testPointFixedYPlane;
+  private final Plane testPointFixedYAbovePlane;
+  private final Plane testPointFixedYBelowPlane;
+  private final Plane testPointFixedXPlane;
+  private final Plane testPointFixedXAbovePlane;
+  private final Plane testPointFixedXBelowPlane;
+  private final Plane testPointFixedZPlane;
+  private final Plane testPointFixedZAbovePlane;
+  private final Plane testPointFixedZBelowPlane;
   
   private final GeoPoint[] edgePoints;
   private final Edge[] shapeStartEdges;
@@ -69,16 +69,16 @@ class GeoComplexPolygon extends GeoBasePolygon {
     this.testPointInSet = testPointInSet;
     this.testPoint = testPoint;
     
-    this.testPointXZPlane = new Plane(0.0, 1.0, 0.0, -testPoint.y);
-    this.testPointYZPlane = new Plane(1.0, 0.0, 0.0, -testPoint.x);
-    this.testPointXYPlane = new Plane(0.0, 0.0, 1.0, -testPoint.z);
+    this.testPointFixedYPlane = new Plane(0.0, 1.0, 0.0, -testPoint.y);
+    this.testPointFixedXPlane = new Plane(1.0, 0.0, 0.0, -testPoint.x);
+    this.testPointFixedZPlane = new Plane(0.0, 0.0, 1.0, -testPoint.z);
     
-    this.testPointXZAbovePlane = new Plane(testPointXZPlane, true);
-    this.testPointXZBelowPlane = new Plane(testPointXZPlane, false);
-    this.testPointYZAbovePlane = new Plane(testPointYZPlane, true);
-    this.testPointYZBelowPlane = new Plane(testPointYZPlane, false);
-    this.testPointXYAbovePlane = new Plane(testPointXYPlane, true);
-    this.testPointXYBelowPlane = new Plane(testPointXYPlane, false);
+    this.testPointFixedYAbovePlane = new Plane(testPointFixedYPlane, true);
+    this.testPointFixedYBelowPlane = new Plane(testPointFixedYPlane, false);
+    this.testPointFixedXAbovePlane = new Plane(testPointFixedXPlane, true);
+    this.testPointFixedXBelowPlane = new Plane(testPointFixedXPlane, false);
+    this.testPointFixedZAbovePlane = new Plane(testPointFixedZPlane, true);
+    this.testPointFixedZBelowPlane = new Plane(testPointFixedZPlane, false);
 
     this.edgePoints = new GeoPoint[pointsList.size()];
     this.shapeStartEdges = new Edge[pointsList.size()];
@@ -124,27 +124,27 @@ class GeoComplexPolygon extends GeoBasePolygon {
     }
     
     // If we're right on top of any of the test planes, we navigate solely on that plane.
-    if (testPointXZPlane.evaluateIsZero(thePoint)) {
+    if (testPointFixedYPlane.evaluateIsZero(thePoint)) {
       // Use the XZ plane exclusively.
-      final LinearCrossingEdgeIterator crossingEdgeIterator = new LinearCrossingEdgeIterator(testPointXZPlane, testPointXZAbovePlane, testPointXZBelowPlane, testPoint, thePoint);
+      final LinearCrossingEdgeIterator crossingEdgeIterator = new LinearCrossingEdgeIterator(testPointFixedYPlane, testPointFixedYAbovePlane, testPointFixedYBelowPlane, testPoint, thePoint);
       // Traverse our way from the test point to the check point.  Use the y tree because that's fixed.
       if (!yTree.traverse(crossingEdgeIterator, testPoint.y)) {
         // Endpoint is on edge
         return true;
       }
       return ((crossingEdgeIterator.crossingCount & 1) == 0)?testPointInSet:!testPointInSet;
-    } else if (testPointYZPlane.evaluateIsZero(thePoint)) {
+    } else if (testPointFixedXPlane.evaluateIsZero(thePoint)) {
       // Use the YZ plane exclusively.
-      final LinearCrossingEdgeIterator crossingEdgeIterator = new LinearCrossingEdgeIterator(testPointYZPlane, testPointYZAbovePlane, testPointYZBelowPlane, testPoint, thePoint);
+      final LinearCrossingEdgeIterator crossingEdgeIterator = new LinearCrossingEdgeIterator(testPointFixedXPlane, testPointFixedXAbovePlane, testPointFixedXBelowPlane, testPoint, thePoint);
       // Traverse our way from the test point to the check point.  Use the x tree because that's fixed.
       if (!xTree.traverse(crossingEdgeIterator, testPoint.x)) {
         // Endpoint is on edge
         return true;
       }
       return ((crossingEdgeIterator.crossingCount & 1) == 0)?testPointInSet:!testPointInSet;
-    } else if (testPointXYPlane.evaluateIsZero(thePoint)) {
+    } else if (testPointFixedZPlane.evaluateIsZero(thePoint)) {
       // Use the XY plane exclusively.
-      final LinearCrossingEdgeIterator crossingEdgeIterator = new LinearCrossingEdgeIterator(testPointXYPlane, testPointXYAbovePlane, testPointXYBelowPlane, testPoint, thePoint);
+      final LinearCrossingEdgeIterator crossingEdgeIterator = new LinearCrossingEdgeIterator(testPointFixedZPlane, testPointFixedZAbovePlane, testPointFixedZBelowPlane, testPoint, thePoint);
       // Traverse our way from the test point to the check point.  Use the z tree because that's fixed.
       if (!zTree.traverse(crossingEdgeIterator, testPoint.z)) {
         // Endpoint is on edge
@@ -153,20 +153,18 @@ class GeoComplexPolygon extends GeoBasePolygon {
       return ((crossingEdgeIterator.crossingCount & 1) == 0)?testPointInSet:!testPointInSet;
     } else {
       
-      System.err.println("isWithin() for check point "+thePoint+", test point "+testPoint);
-      
       // We need to use two planes to get there.  We don't know which two planes will do it but we can figure it out.
       final Plane travelPlaneFixedX = new Plane(1.0, 0.0, 0.0, -thePoint.x);
       final Plane travelPlaneFixedY = new Plane(0.0, 1.0, 0.0, -thePoint.y);
       final Plane travelPlaneFixedZ = new Plane(0.0, 0.0, 1.0, -thePoint.z);
 
       // Find the intersection points for each one of these and the complementary test point planes.
-      final GeoPoint[] XZIntersectionsYZ = travelPlaneFixedX.findIntersections(planetModel, testPointYZPlane);
-      final GeoPoint[] XZIntersectionsXY = travelPlaneFixedX.findIntersections(planetModel, testPointXYPlane);
-      final GeoPoint[] YZIntersectionsXZ = travelPlaneFixedY.findIntersections(planetModel, testPointXZPlane);
-      final GeoPoint[] YZIntersectionsXY = travelPlaneFixedY.findIntersections(planetModel, testPointXYPlane);
-      final GeoPoint[] XYIntersectionsYZ = travelPlaneFixedZ.findIntersections(planetModel, testPointYZPlane);
-      final GeoPoint[] XYIntersectionsXZ = travelPlaneFixedZ.findIntersections(planetModel, testPointXZPlane);
+      final GeoPoint[] XIntersectionsY = travelPlaneFixedX.findIntersections(planetModel, testPointFixedYPlane);
+      final GeoPoint[] XIntersectionsZ = travelPlaneFixedX.findIntersections(planetModel, testPointFixedZPlane);
+      final GeoPoint[] YIntersectionsX = travelPlaneFixedY.findIntersections(planetModel, testPointFixedXPlane);
+      final GeoPoint[] YIntersectionsZ = travelPlaneFixedY.findIntersections(planetModel, testPointFixedZPlane);
+      final GeoPoint[] ZIntersectionsX = travelPlaneFixedZ.findIntersections(planetModel, testPointFixedXPlane);
+      final GeoPoint[] ZIntersectionsY = travelPlaneFixedZ.findIntersections(planetModel, testPointFixedYPlane);
 
       // There will be multiple intersection points found.  We choose the one that has the lowest total distance, as measured in delta X, delta Y, and delta Z.
       double bestDistance = Double.MAX_VALUE;
@@ -180,107 +178,106 @@ class GeoComplexPolygon extends GeoBasePolygon {
       Tree secondLegTree = null;
       GeoPoint intersectionPoint = null;
       
-      for (final GeoPoint p : XZIntersectionsYZ) {
-        // Travel would be in XZ plane (fixed y) then in YZ (fixed x)
-        final double newDistance = Math.abs(testPoint.y - p.y) + Math.abs(thePoint.x - p.x);
+      for (final GeoPoint p : XIntersectionsY) {
+        // Travel would be in YZ plane (fixed x) then in XZ (fixed y)
+        final double newDistance = Math.abs(testPoint.x - p.x) + Math.abs(thePoint.y - p.y);
         if (newDistance < bestDistance) {
           bestDistance = newDistance;
           firstLegValue = testPoint.y;
           secondLegValue = thePoint.x;
-          firstLegPlane = testPointXZPlane;
-          firstLegAbovePlane = testPointXZAbovePlane;
-          firstLegBelowPlane = testPointXZBelowPlane;
+          firstLegPlane = testPointFixedYPlane;
+          firstLegAbovePlane = testPointFixedYAbovePlane;
+          firstLegBelowPlane = testPointFixedYBelowPlane;
           secondLegPlane = travelPlaneFixedX;
           firstLegTree = yTree;
           secondLegTree = xTree;
           intersectionPoint = p;
         }
       }
-      for (final GeoPoint p : XZIntersectionsXY) {
-        // Travel would be in XZ plane (fixed y) then in XY (fixed z)
-        final double newDistance = Math.abs(testPoint.y - p.y) + Math.abs(thePoint.z - p.z);
+      for (final GeoPoint p : XIntersectionsZ) {
+        // Travel would be in YZ plane (fixed x) then in XY (fixed z)
+        final double newDistance = Math.abs(testPoint.x - p.x) + Math.abs(thePoint.z - p.z);
         if (newDistance < bestDistance) {
           bestDistance = newDistance;
-          firstLegValue = testPoint.y;
-          secondLegValue = thePoint.z;
-          firstLegPlane = testPointXZPlane;
-          firstLegAbovePlane = testPointXZAbovePlane;
-          firstLegBelowPlane = testPointXZBelowPlane;
-          secondLegPlane = travelPlaneFixedZ;
-          firstLegTree = yTree;
-          secondLegTree = zTree;
+          firstLegValue = testPoint.z;
+          secondLegValue = thePoint.x;
+          firstLegPlane = testPointFixedZPlane;
+          firstLegAbovePlane = testPointFixedZAbovePlane;
+          firstLegBelowPlane = testPointFixedZBelowPlane;
+          secondLegPlane = travelPlaneFixedX;
+          firstLegTree = zTree;
+          secondLegTree = xTree;
           intersectionPoint = p;
         }
       }
-      for (final GeoPoint p : YZIntersectionsXZ) {
-        // Travel would be in YZ plane (fixed x) then in XZ (fixed y)
-        final double newDistance = Math.abs(testPoint.x - p.x) + Math.abs(thePoint.y - p.y);
+      for (final GeoPoint p : YIntersectionsX) {
+        // Travel would be in XZ plane (fixed y) then in YZ (fixed x)
+        final double newDistance = Math.abs(testPoint.y - p.y) + Math.abs(thePoint.x - p.x);
         if (newDistance < bestDistance) {
           bestDistance = newDistance;
           firstLegValue = testPoint.x;
           secondLegValue = thePoint.y;
-          firstLegPlane = testPointYZPlane;
-          firstLegAbovePlane = testPointYZAbovePlane;
-          firstLegBelowPlane = testPointYZBelowPlane;
+          firstLegPlane = testPointFixedXPlane;
+          firstLegAbovePlane = testPointFixedXAbovePlane;
+          firstLegBelowPlane = testPointFixedXBelowPlane;
           secondLegPlane = travelPlaneFixedY;
           firstLegTree = xTree;
           secondLegTree = yTree;
           intersectionPoint = p;
         }
       }
-      for (final GeoPoint p : YZIntersectionsXY) {
-        // Travel would be in YZ plane (fixed x) then in XY (fixed z)
-        final double newDistance = Math.abs(testPoint.x - p.x) + Math.abs(thePoint.z - p.z);
+      for (final GeoPoint p : YIntersectionsZ) {
+        // Travel would be in XZ plane (fixed y) then in XY (fixed z)
+        final double newDistance = Math.abs(testPoint.y - p.y) + Math.abs(thePoint.z - p.z);
         if (newDistance < bestDistance) {
           bestDistance = newDistance;
-          firstLegValue = testPoint.x;
-          secondLegValue = thePoint.z;
-          firstLegPlane = testPointYZPlane;
-          firstLegAbovePlane = testPointYZAbovePlane;
-          firstLegBelowPlane = testPointYZBelowPlane;
-          secondLegPlane = travelPlaneFixedZ;
-          firstLegTree = xTree;
-          secondLegTree = zTree;
+          firstLegValue = testPoint.z;
+          secondLegValue = thePoint.y;
+          firstLegPlane = testPointFixedZPlane;
+          firstLegAbovePlane = testPointFixedZAbovePlane;
+          firstLegBelowPlane = testPointFixedZBelowPlane;
+          secondLegPlane = travelPlaneFixedY;
+          firstLegTree = zTree;
+          secondLegTree = yTree;
           intersectionPoint = p;
         }
       }
-      for (final GeoPoint p : XYIntersectionsYZ) {
+      for (final GeoPoint p : ZIntersectionsX) {
         // Travel would be in XY plane (fixed z) then in YZ (fixed x)
         final double newDistance = Math.abs(testPoint.z - p.z) + Math.abs(thePoint.x - p.x);
         if (newDistance < bestDistance) {
           bestDistance = newDistance;
-          firstLegValue = testPoint.z;
-          secondLegValue = thePoint.x;
-          firstLegPlane = testPointXYPlane;
-          firstLegAbovePlane = testPointXYAbovePlane;
-          firstLegBelowPlane = testPointXYBelowPlane;
-          secondLegPlane = travelPlaneFixedX;
-          firstLegTree = zTree;
-          secondLegTree = xTree;
+          firstLegValue = testPoint.x;
+          secondLegValue = thePoint.z;
+          firstLegPlane = testPointFixedXPlane;
+          firstLegAbovePlane = testPointFixedXAbovePlane;
+          firstLegBelowPlane = testPointFixedXBelowPlane;
+          secondLegPlane = travelPlaneFixedZ;
+          firstLegTree = xTree;
+          secondLegTree = zTree;
           intersectionPoint = p;
         }
       }
-      for (final GeoPoint p : XYIntersectionsXZ) {
+      for (final GeoPoint p : ZIntersectionsY) {
         // Travel would be in XY plane (fixed z) then in XZ (fixed y)
         final double newDistance = Math.abs(testPoint.z - p.z) + Math.abs(thePoint.y - p.y);
         if (newDistance < bestDistance) {
           bestDistance = newDistance;
-          firstLegValue = testPoint.z;
-          secondLegValue = thePoint.y;
-          firstLegPlane = testPointXYPlane;
-          firstLegAbovePlane = testPointXYAbovePlane;
-          firstLegBelowPlane = testPointXYBelowPlane;
-          secondLegPlane = travelPlaneFixedY;
-          firstLegTree = zTree;
-          secondLegTree = yTree;
+          firstLegValue = testPoint.y;
+          secondLegValue = thePoint.z;
+          firstLegPlane = testPointFixedYPlane;
+          firstLegAbovePlane = testPointFixedYAbovePlane;
+          firstLegBelowPlane = testPointFixedYBelowPlane;
+          secondLegPlane = travelPlaneFixedZ;
+          firstLegTree = yTree;
+          secondLegTree = zTree;
           intersectionPoint = p;
         }
       }
 
+      assert bestDistance > 0.0 : "Best distance should not be zero unless on single plane";
       assert bestDistance < Double.MAX_VALUE : "Couldn't find an intersection point of any kind";
       
-      System.err.println("Best distance: "+bestDistance);
-      
       final DualCrossingEdgeIterator edgeIterator = new DualCrossingEdgeIterator(firstLegPlane, firstLegAbovePlane, firstLegBelowPlane, secondLegPlane, testPoint, thePoint, intersectionPoint);
       if (!firstLegTree.traverse(edgeIterator, firstLegValue)) {
         return true;
@@ -392,7 +389,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
       this.planeBounds.addPoint(startPoint);
       this.planeBounds.addPoint(endPoint);
       this.plane.recordBounds(pm, this.planeBounds, this.startPlane, this.endPlane);
-      System.err.println("Recording edge "+this+" from "+startPoint+" to "+endPoint+"; bounds = "+planeBounds);
+      //System.err.println("Recording edge "+this+" from "+startPoint+" to "+endPoint+"; bounds = "+planeBounds);
     }
   }
   
@@ -632,12 +629,14 @@ class GeoComplexPolygon extends GeoBasePolygon {
     public ZTree() {
     }
     
+    /*
     @Override
     public boolean traverse(final EdgeIterator edgeIterator, final double value) {
       System.err.println("Traversing in Z, value= "+value+"...");
       return super.traverse(edgeIterator, value);
     }
-
+    */
+    
     @Override
     protected double getMinimum(final Edge edge) {
       return edge.planeBounds.getMinimumZ();
@@ -657,11 +656,13 @@ class GeoComplexPolygon extends GeoBasePolygon {
     public YTree() {
     }
 
+    /*
     @Override
     public boolean traverse(final EdgeIterator edgeIterator, final double value) {
       System.err.println("Traversing in Y, value= "+value+"...");
       return super.traverse(edgeIterator, value);
     }
+    */
     
     @Override
     protected double getMinimum(final Edge edge) {
@@ -682,11 +683,13 @@ class GeoComplexPolygon extends GeoBasePolygon {
     public XTree() {
     }
     
+    /*
     @Override
     public boolean traverse(final EdgeIterator edgeIterator, final double value) {
       System.err.println("Traversing in X, value= "+value+"...");
       return super.traverse(edgeIterator, value);
     }
+    */
     
     @Override
     protected double getMinimum(final Edge edge) {
@@ -909,6 +912,11 @@ class GeoComplexPolygon extends GeoBasePolygon {
       this.thePoint = thePoint;
       this.intersectionPoint = intersectionPoint;
       
+      //System.err.println("Intersection point = "+intersectionPoint);
+        
+      assert travelPlane.evaluateIsZero(intersectionPoint) : "intersection point must be on travel plane";
+      assert testPointPlane.evaluateIsZero(intersectionPoint) : "intersection point must be on test point plane";
+        
       assert !testPoint.isNumericallyIdentical(intersectionPoint) : "test point is the same as intersection point";
       assert !thePoint.isNumericallyIdentical(intersectionPoint) : "check point is same is intersection point";
 
@@ -981,10 +989,10 @@ class GeoComplexPolygon extends GeoBasePolygon {
     
     @Override
     public boolean matches(final Edge edge) {
-      System.err.println("Processing edge "+edge+", startpoint="+edge.startPoint+" endpoint="+edge.endPoint);
+      //System.err.println("Processing edge "+edge+", startpoint="+edge.startPoint+" endpoint="+edge.endPoint);
       // Early exit if the point is on the edge.
       if (thePoint != null && edge.plane.evaluateIsZero(thePoint) && edge.startPlane.isWithin(thePoint) && edge.endPlane.isWithin(thePoint)) {
-        System.err.println(" Check point is on edge: isWithin = true");
+        //System.err.println(" Check point is on edge: isWithin = true");
         return false;
       }
       // If the intersection point lies on this edge, we should still be able to consider crossing points only.
@@ -992,10 +1000,10 @@ class GeoComplexPolygon extends GeoBasePolygon {
       // for at least one of the two planes in order to be a legitimate crossing of the combined path.
       final GeoPoint[] crossingPoints;
       if (isSecondLeg) {
-        System.err.println(" check point plane = "+travelPlane);
+        //System.err.println(" check point plane = "+travelPlane);
         crossingPoints = travelPlane.findCrossings(planetModel, edge.plane, checkPointCutoffPlane, checkPointOtherCutoffPlane, edge.startPlane, edge.endPlane);
       } else {
-        System.err.println(" test point plane = "+testPointPlane);
+        //System.err.println(" test point plane = "+testPointPlane);
         crossingPoints = testPointPlane.findCrossings(planetModel, edge.plane, testPointCutoffPlane, testPointOtherCutoffPlane, edge.startPlane, edge.endPlane);
       }
       if (crossingPoints != null) {
@@ -1003,15 +1011,15 @@ class GeoComplexPolygon extends GeoBasePolygon {
         for (final GeoPoint crossingPoint : crossingPoints) {
           countCrossingPoint(crossingPoint, edge);
         }
-        System.err.println(" All crossing points processed");
+        //System.err.println(" All crossing points processed");
       } else {
-        System.err.println(" No crossing points!");
+        //System.err.println(" No crossing points!");
       }
       return true;
     }
 
     private void countCrossingPoint(final GeoPoint crossingPoint, final Edge edge) {
-      System.err.println(" Crossing point "+crossingPoint);
+      //System.err.println(" Crossing point "+crossingPoint);
       // We consider crossing points only in this method.
       // Unlike the linear case, there are additional cases when:
       // (1) The crossing point and the intersection point are the same, but are not the endpoint of an edge;
@@ -1025,14 +1033,14 @@ class GeoComplexPolygon extends GeoBasePolygon {
       // In either case, we have to be sure to count each edge only once, since it might appear in both the
       // first leg and the second.  If the first leg can process it, it should, and the second should skip it.
       if (crossingPoint.isNumericallyIdentical(intersectionPoint)) {
-        System.err.println(" Crosses intersection point.");
+        //System.err.println(" Crosses intersection point.");
         if (isSecondLeg) {
           // See whether this edge would have been processed in the first leg; if so, we skip it.
           final GeoPoint[] firstLegCrossings = testPointPlane.findCrossings(planetModel, edge.plane, testPointCutoffPlane, testPointOtherCutoffPlane, edge.startPlane, edge.endPlane);
           for (final GeoPoint firstLegCrossing : firstLegCrossings) {
             if (firstLegCrossing.isNumericallyIdentical(intersectionPoint)) {
               // We already processed it, so we're done here.
-              System.err.println("  Already processed on previous leg: exit");
+              //System.err.println("  Already processed on previous leg: exit");
               return;
             }
           }
@@ -1061,7 +1069,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
       }
         
       if (crossingPoint.isNumericallyIdentical(edge.startPoint)) {
-        System.err.println(" Crossing point = edge.startPoint");
+        //System.err.println(" Crossing point = edge.startPoint");
         // We have to figure out if this crossing should be counted.
           
         // Does the crossing for this edge go up, or down?  Or can't we tell?
@@ -1073,6 +1081,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
         assert !(insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length > 0 && outsideTestPointPlaneIntersections.length + outsideTravelPlaneIntersections.length > 0) : "edge that ends in a crossing can't both up and down";
           
         if (insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length == 0 && outsideTestPointPlaneIntersections.length + outsideTravelPlaneIntersections.length == 0) {
+          //System.err.println(" No inside or outside crossings found");
           return;
         }
 
@@ -1117,6 +1126,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
             // Found it!
             // Both edges will try to contribute to the crossing count.  By convention, we'll only include the earlier one.
             // Since we're the latter point, we exit here in that case.
+            //System.err.println(" Earlier point fired, so this one shouldn't");
             return;
           }
         }
@@ -1127,11 +1137,14 @@ class GeoComplexPolygon extends GeoBasePolygon {
           
         final boolean assessEdgeInside = assessInsideTestPointIntersections.length + assessInsideTravelIntersections.length > 0;
         if (assessEdgeInside != edgeCrossesInside) {
+          //System.err.println(" Incrementing crossing count");
           crossingCount++;
+        } else {
+          //System.err.println(" Entered and exited on same side");
         }
           
       } else if (crossingPoint.isNumericallyIdentical(edge.endPoint)) {
-        System.err.println(" Crossing point = edge.endPoint");
+        //System.err.println(" Crossing point = edge.endPoint");
         // Figure out if the crossing should be counted.
           
         // Does the crossing for this edge go up, or down?  Or can't we tell?
@@ -1144,6 +1157,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
         //assert !(insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length > 0 && outsideTestPointPlaneIntersections.length + outsideTravelPlaneIntersections.length > 0) : "edge that ends in a crossing can't go both up and down: insideTestPointPlaneIntersections: "+insideTestPointPlaneIntersections.length+" insideTravelPlaneIntersections: "+insideTravelPlaneIntersections.length+" outsideTestPointPlaneIntersections: "+outsideTestPointPlaneIntersections.length+" outsideTravelPlaneIntersections: "+outsideTravelPlaneIntersections.length;
           
         if (insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length == 0 && outsideTestPointPlaneIntersections.length + outsideTravelPlaneIntersections.length == 0) {
+          //System.err.println(" No inside or outside crossings found");
           return;
         }
 
@@ -1183,10 +1197,13 @@ class GeoComplexPolygon extends GeoBasePolygon {
 
         final boolean assessEdgeInside = assessInsideTestPointIntersections.length + assessInsideTravelIntersections.length > 0;
         if (assessEdgeInside != edgeCrossesInside) {
+          //System.err.println(" Incrementing crossing count");
           crossingCount++;
+        } else {
+          //System.err.println(" Entered and exited on same side");
         }
       } else {
-        System.err.println(" Not a special case: incrementing crossing count");
+        //System.err.println(" Not a special case: incrementing crossing count");
         // Not a special case, so we can safely count a crossing.
         crossingCount++;
       }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/da896f80/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
index 53fc246..b325e43 100755
--- a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
+++ b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
@@ -189,9 +189,6 @@ public class GeoPolygonTest {
     shapes.add(new GeoPolygonFactory.PolygonDescription(points));
     
     c = GeoPolygonFactory.makeLargeGeoPolygon(PlanetModel.SPHERE, shapes);
-    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.35);
-    assertFalse(c.isWithin(gp));
-
     // Sample some points within
     gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.45);
     assertTrue(c.isWithin(gp));


[07/26] lucene-solr:master: Handle the case where the last leg lands on an edge.

Posted by kw...@apache.org.
Handle the case where the last leg lands on an edge.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/8b50cb8a
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/8b50cb8a
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/8b50cb8a

Branch: refs/heads/master
Commit: 8b50cb8a8ff3c916a794575bda3cc145a7177fcc
Parents: d41537d
Author: Karl Wright <Da...@gmail.com>
Authored: Mon Apr 25 13:05:12 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Mon Apr 25 13:05:12 2016 -0400

----------------------------------------------------------------------
 .../spatial3d/geom/GeoComplexPolygon.java       | 58 +++++++++++++-------
 1 file changed, 38 insertions(+), 20 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/8b50cb8a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
index 122a6eb..229f9f4 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
@@ -133,31 +133,34 @@ class GeoComplexPolygon extends GeoBasePolygon {
       // Use the XZ plane exclusively.
       final SidedPlane testPointCutoff =  new SidedPlane(thePoint, testPointXZPlane, testPoint);
       final SidedPlane checkPointCutoff = new SidedPlane(testPoint, testPointXZPlane, thePoint);
-      // Note: need to detect condition where edge endpoint is the check point!
-      // MHL
-      final CrossingEdgeIterator crossingEdgeIterator = new CrossingEdgeIterator(testPointXZPlane, testPointCutoff, checkPointCutoff);
+      final CrossingEdgeIterator crossingEdgeIterator = new CrossingEdgeIterator(testPointXZPlane, testPointCutoff, checkPointCutoff, thePoint);
       // Traverse our way from the test point to the check point.  Use the y tree because that's fixed.
-      yTree.traverse(crossingEdgeIterator, testPoint.y, testPoint.y);
+      if (!yTree.traverse(crossingEdgeIterator, testPoint.y, testPoint.y)) {
+        // Endpoint is on edge
+        return true;
+      }
       return ((crossingEdgeIterator.crossingCount & 1) == 0)?testPointInSet:!testPointInSet;
     } else if (testPointYZPlane.evaluateIsZero(thePoint)) {
       // Use the YZ plane exclusively.
       final SidedPlane testPointCutoff =  new SidedPlane(thePoint, testPointYZPlane, testPoint);
       final SidedPlane checkPointCutoff = new SidedPlane(testPoint, testPointYZPlane, thePoint);
-      // Note: need to detect condition where edge endpoint is the check point!
-      // MHL
-      final CrossingEdgeIterator crossingEdgeIterator = new CrossingEdgeIterator(testPointYZPlane, testPointCutoff, checkPointCutoff);
+      final CrossingEdgeIterator crossingEdgeIterator = new CrossingEdgeIterator(testPointYZPlane, testPointCutoff, checkPointCutoff, thePoint);
       // Traverse our way from the test point to the check point.  Use the x tree because that's fixed.
-      xTree.traverse(crossingEdgeIterator, testPoint.x, testPoint.x);
+      if (!xTree.traverse(crossingEdgeIterator, testPoint.x, testPoint.x)) {
+        // Endpoint is on edge
+        return true;
+      }
       return ((crossingEdgeIterator.crossingCount & 1) == 0)?testPointInSet:!testPointInSet;
     } else if (testPointXYPlane.evaluateIsZero(thePoint)) {
       // Use the XY plane exclusively.
       final SidedPlane testPointCutoff =  new SidedPlane(thePoint, testPointXYPlane, testPoint);
       final SidedPlane checkPointCutoff = new SidedPlane(testPoint, testPointXYPlane, thePoint);
-      // Note: need to detect condition where edge endpoint is the check point!
-      // MHL
-      final CrossingEdgeIterator crossingEdgeIterator = new CrossingEdgeIterator(testPointXYPlane, testPointCutoff, checkPointCutoff);
+      final CrossingEdgeIterator crossingEdgeIterator = new CrossingEdgeIterator(testPointXYPlane, testPointCutoff, checkPointCutoff, thePoint);
       // Traverse our way from the test point to the check point.  Use the z tree because that's fixed.
-      zTree.traverse(crossingEdgeIterator, testPoint.z, testPoint.z);
+      if (!zTree.traverse(crossingEdgeIterator, testPoint.z, testPoint.z)) {
+        // Endpoint is on edge
+        return true;
+      }
       return ((crossingEdgeIterator.crossingCount & 1) == 0)?testPointInSet:!testPointInSet;
     } else {
       
@@ -180,8 +183,11 @@ class GeoComplexPolygon extends GeoBasePolygon {
         // MHL
         final CrossingEdgeIterator testPointEdgeIterator = new CrossingEdgeIterator(testPointYZPlane, testPointCutoffPlane, testPointOtherCutoffPlane);
         xTree.traverse(testPointEdgeIterator, testPoint.x, testPoint.x);
-        final CrossingEdgeIterator checkPointEdgeIterator = new CrossingEdgeIterator(travelPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane);
-        yTree.traverse(checkPointEdgeIterator, thePoint.y, thePoint.y);
+        final CrossingEdgeIterator checkPointEdgeIterator = new CrossingEdgeIterator(travelPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane, thePoint);
+        if (!yTree.traverse(checkPointEdgeIterator, thePoint.y, thePoint.y)) {
+          // Endpoint is on edge
+          return true;
+        }
         return (((testPointEdgeIterator.crossingCount + checkPointEdgeIterator.crossingCount) & 1) == 0)?testPointInSet:!testPointInSet;
       } else if (xDelta + zDelta <= xDelta + yDelta && xDelta + zDelta <= zDelta + yDelta) {
         // Travel in X and Z
@@ -200,8 +206,11 @@ class GeoComplexPolygon extends GeoBasePolygon {
         // MHL
         final CrossingEdgeIterator testPointEdgeIterator = new CrossingEdgeIterator(testPointXYPlane, testPointCutoffPlane, testPointOtherCutoffPlane);
         zTree.traverse(testPointEdgeIterator, testPoint.z, testPoint.z);
-        final CrossingEdgeIterator checkPointEdgeIterator = new CrossingEdgeIterator(travelPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane);
-        xTree.traverse(checkPointEdgeIterator, thePoint.x, thePoint.x);
+        final CrossingEdgeIterator checkPointEdgeIterator = new CrossingEdgeIterator(travelPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane, thePoint);
+        if (!xTree.traverse(checkPointEdgeIterator, thePoint.x, thePoint.x)) {
+          // Endpoint is on edge
+          return true;
+        }
         return (((testPointEdgeIterator.crossingCount + checkPointEdgeIterator.crossingCount) & 1) == 0)?testPointInSet:!testPointInSet;
       } else if (yDelta + zDelta <= xDelta + yDelta && yDelta + zDelta <= xDelta + zDelta) {
         // Travel in Y and Z
@@ -216,12 +225,15 @@ class GeoComplexPolygon extends GeoBasePolygon {
         assert intersectionPoints.length != 1 : "wrong number of intersection points";
         final SidedPlane testPointOtherCutoffPlane = new SidedPlane(testPoint, testPointXZPlane, intersectionPoints[0]);
         final SidedPlane checkPointOtherCutoffPlane = new SidedPlane(thePoint, travelPlane, intersectionPoints[0]);
-        // Note: we need to handle the cases where end point of the leg sits on an edge!
+        // Note: we need to handle the cases where end point of the first leg sits on an edge!
         // MHL
         final CrossingEdgeIterator testPointEdgeIterator = new CrossingEdgeIterator(testPointXZPlane, testPointCutoffPlane, testPointOtherCutoffPlane);
         yTree.traverse(testPointEdgeIterator, testPoint.y, testPoint.y);
-        final CrossingEdgeIterator checkPointEdgeIterator = new CrossingEdgeIterator(travelPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane);
-        zTree.traverse(checkPointEdgeIterator, thePoint.z, thePoint.z);
+        final CrossingEdgeIterator checkPointEdgeIterator = new CrossingEdgeIterator(travelPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane, thePoint);
+        if (!zTree.traverse(checkPointEdgeIterator, thePoint.z, thePoint.z)) {
+          // Endpoint is on edge
+          return true;
+        }
         return (((testPointEdgeIterator.crossingCount + checkPointEdgeIterator.crossingCount) & 1) == 0)?testPointInSet:!testPointInSet;
       }
     }
@@ -584,19 +596,25 @@ class GeoComplexPolygon extends GeoBasePolygon {
     private final Plane belowPlane;
     private final Membership bound1;
     private final Membership bound2;
+    private final GeoPoint thePoint;
     
     public int crossingCount = 0;
     
-    public CrossingEdgeIterator(final Plane plane, final Membership bound1, final Membership bound2) {
+    public CrossingEdgeIterator(final Plane plane, final Membership bound1, final Membership bound2, final GeoPoint thePoint) {
       this.plane = plane;
       this.abovePlane = new Plane(plane, true);
       this.belowPlane = new Plane(plane, false);
       this.bound1 = bound1;
       this.bound2 = bound2;
+      this.thePoint = thePoint;
     }
     
     @Override
     public boolean matches(final Edge edge) {
+      // Early exit if the point is on the edge.
+      if (edge.plane.evaluateIsZero(thePoint) && edge.startPlane.isWithin(thePoint) && edge.endPlane.isWithin(thePoint)) {
+        return false;
+      }
       final GeoPoint[] crossingPoints = plane.findCrossings(planetModel, edge.plane, bound1, bound2, edge.startPlane, edge.endPlane);
       if (crossingPoints != null) {
         // We need to handle the endpoint case, which is quite tricky.


[25/26] lucene-solr:master: Fix javadoc

Posted by kw...@apache.org.
Fix javadoc


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/7d33e774
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/7d33e774
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/7d33e774

Branch: refs/heads/master
Commit: 7d33e774c2a8de638be4179aea614d43442749d7
Parents: 31176d1
Author: Karl Wright <Da...@gmail.com>
Authored: Thu Apr 28 19:58:55 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Thu Apr 28 19:58:55 2016 -0400

----------------------------------------------------------------------
 .../java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7d33e774/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
index 661953a..c7197d8 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
@@ -598,8 +598,8 @@ class GeoComplexPolygon extends GeoBasePolygon {
     }
     
     /** Compare a node against a subrange of a new edge.
-     * @param node is the node to compare.
-     * @param newEdge is the edge being added.
+     * @param nodeMinimumValue is the node's minimum value.
+     * @param nodeMaximumValue is the node's maximum value.
      * @param minimumValue is the minimum value for the edge being added.
      * @param maximumValue is the maximum value for the edge being added.
      * @return the comparison result.


[18/26] lucene-solr:master: Fix initialization bugs that prevented the code from working.

Posted by kw...@apache.org.
Fix initialization bugs that prevented the code from working.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/f896afc8
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/f896afc8
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/f896afc8

Branch: refs/heads/master
Commit: f896afc88b36febafa3595c0277e938bbf9ee2e5
Parents: 9dd7921
Author: Karl Wright <Da...@gmail.com>
Authored: Wed Apr 27 17:19:30 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Wed Apr 27 17:19:30 2016 -0400

----------------------------------------------------------------------
 .../org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java  | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f896afc8/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
index d8c8e75..3a89ee5 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
@@ -864,9 +864,9 @@ class GeoComplexPolygon extends GeoBasePolygon {
         
       this.testPointCutoffPlane = new SidedPlane(thePoint, testPointPlane, testPoint);
       this.checkPointCutoffPlane = new SidedPlane(testPoint, travelPlane, thePoint);
-        
-      this.testPointOtherCutoffPlane = new SidedPlane(testPoint, testPointPlane, intersectionPoint);
-      this.checkPointOtherCutoffPlane = new SidedPlane(thePoint, travelPlane, intersectionPoint);
+
+      this.testPointOtherCutoffPlane = new SidedPlane(testPoint, travelPlane, -(travelPlane.x * intersectionPoint.x + travelPlane.y * intersectionPoint.y + travelPlane.z * intersectionPoint.z));
+      this.checkPointOtherCutoffPlane = new SidedPlane(thePoint, testPointPlane, -(testPointPlane.x * intersectionPoint.x + testPointPlane.y * intersectionPoint.y + testPointPlane.z * intersectionPoint.z));
         
       // Figure out which of the above/below planes are inside vs. outside.  To do this,
       // we look for the point that is within the bounds of the testPointPlane and travelPlane.  The two sides that intersected there are the inside
@@ -882,7 +882,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
       final GeoPoint[] belowAbove = travelBelowPlane.findIntersections(planetModel, testPointAbovePlane, testPointCutoffPlane, testPointOtherCutoffPlane, checkPointCutoffPlane, checkPointOtherCutoffPlane);
       assert belowAbove != null : "Below + above should not be coplanar";
       
-      assert aboveAbove.length + aboveBelow.length + belowBelow.length + belowAbove.length == 1 : "Can be exactly one inside point";
+      assert aboveAbove.length + aboveBelow.length + belowBelow.length + belowAbove.length == 1 : "Can be exactly one inside point, instead was: aa="+aboveAbove.length+" ab=" + aboveBelow.length+" bb="+ belowBelow.length+" ba=" + belowAbove.length;
       
       final GeoPoint insideIntersection;
       if (aboveAbove.length > 0) {


[14/26] lucene-solr:master: Restructure DualCrossing counter so that the plane inside and outside boundaries are used to detect inside and outside crossings. Also identify the case for special treatment of edges lying on the intersection point.

Posted by kw...@apache.org.
Restructure DualCrossing counter so that the plane inside and outside boundaries are used to detect inside and outside crossings.  Also identify the case for special treatment of edges lying on the intersection point.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/117f79dd
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/117f79dd
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/117f79dd

Branch: refs/heads/master
Commit: 117f79ddd2cf019f9bb6ba2083361eb2b8a339f3
Parents: 478e0e1
Author: Karl Wright <Da...@gmail.com>
Authored: Tue Apr 26 13:49:47 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Tue Apr 26 13:49:47 2016 -0400

----------------------------------------------------------------------
 .../spatial3d/geom/GeoComplexPolygon.java       | 285 +++++++++++--------
 1 file changed, 163 insertions(+), 122 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/117f79dd/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
index 7e4d01c..b5c29d6 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
@@ -729,6 +729,9 @@ class GeoComplexPolygon extends GeoBasePolygon {
     private final SidedPlane testPointOtherCutoffPlane;
     private final SidedPlane checkPointOtherCutoffPlane;
 
+    private final SidedPlane insideTestPointCutoffPlane;
+    private final SidedPlane insideTravelCutoffPlane;
+    
     public int crossingCount = 0;
 
     public DualCrossingEdgeIterator(final Plane testPointPlane, final Plane testPointAbovePlane, final Plane testPointBelowPlane,
@@ -762,28 +765,36 @@ class GeoComplexPolygon extends GeoBasePolygon {
       
       assert aboveAbove.length + aboveBelow.length + belowBelow.length + belowAbove.length == 1 : "Can be exactly one inside point";
       
+      final GeoPoint insideIntersection;
       if (aboveAbove.length > 0) {
         travelInsidePlane = travelAbovePlane;
         testPointInsidePlane = testPointAbovePlane;
         travelOutsidePlane = travelBelowPlane;
         testPointOutsidePlane = testPointBelowPlane;
+        insideIntersection = aboveAbove[0];
       } else if (aboveBelow.length > 0) {
         travelInsidePlane = travelAbovePlane;
         testPointInsidePlane = testPointBelowPlane;
         travelOutsidePlane = travelBelowPlane;
         testPointOutsidePlane = testPointAbovePlane;
+        insideIntersection = aboveBelow[0];
       } else if (belowBelow.length > 0) {
         travelInsidePlane = travelBelowPlane;
         testPointInsidePlane = testPointBelowPlane;
         travelOutsidePlane = travelAbovePlane;
         testPointOutsidePlane = testPointAbovePlane;
+        insideIntersection = belowBelow[0];
       } else {
         travelInsidePlane = travelBelowPlane;
         testPointInsidePlane = testPointAbovePlane;
         travelOutsidePlane = travelAbovePlane;
         testPointOutsidePlane = testPointBelowPlane;
+        insideIntersection = belowAbove[0];
       }
-        
+      
+      insideTravelCutoffPlane = new SidedPlane(thePoint, travelInsidePlane, insideIntersection);
+      insideTestPointCutoffPlane = new SidedPlane(testPoint, testPointInsidePlane, insideIntersection);
+
     }
 
     public void setSecondLeg() {
@@ -796,6 +807,9 @@ class GeoComplexPolygon extends GeoBasePolygon {
       if (thePoint != null && edge.plane.evaluateIsZero(thePoint) && edge.startPlane.isWithin(thePoint) && edge.endPlane.isWithin(thePoint)) {
         return false;
       }
+      // If the intersection point lies on this edge, we should still be able to consider crossing points only.
+      // Even if an intersection point is eliminated because it's not a crossing of one plane, it will have to be a crossing
+      // for at least one of the two planes in order to be a legitimate crossing of the combined path.
       final GeoPoint[] crossingPoints;
       if (isSecondLeg) {
         crossingPoints = travelPlane.findCrossings(planetModel, edge.plane, checkPointCutoffPlane, checkPointOtherCutoffPlane, edge.startPlane, edge.endPlane);
@@ -812,139 +826,166 @@ class GeoComplexPolygon extends GeoBasePolygon {
     }
 
     private void countCrossingPoint(final GeoPoint crossingPoint, final Edge edge) {
-      final Plane plane;
-      final Plane insidePlane;
-      final Plane outsidePlane;
-      final SidedPlane bound1;
-      final SidedPlane bound2;
-      if (isSecondLeg) {
-        plane = travelPlane;
-        insidePlane = travelInsidePlane;
-        outsidePlane = travelOutsidePlane;
-        bound1 = checkPointCutoffPlane;
-        bound2 = checkPointOtherCutoffPlane;
+      // We consider crossing points only in this method.
+      // Unlike the linear case, there are additional cases when:
+      // (1) The crossing point and the intersection point are the same, but are not the endpoint of an edge;
+      // (2) The crossing point and the intersection point are the same, and they *are* the endpoint of an edge.
+      // The other logical difference is that crossings of all kinds have to be considered so that:
+      // (a) both inside edges are considered together at all times;
+      // (b) both outside edges are considered together at all times;
+      // (c) inside edge crossings that are between the other leg's inside and outside edge are ignored.
+      if (crossingPoint.isNumericallyIdentical(intersectionPoint)) {
+        // Intersection point crossing
+        
+        // MHL to deal with intersection point crossing!!
+        
       } else {
-        plane = testPointPlane;
-        insidePlane = testPointInsidePlane;
-        outsidePlane = testPointOutsidePlane;
-        bound1 = testPointCutoffPlane;
-        bound2 = testPointOtherCutoffPlane;
-      }
-      
-      // MHL - this code below is temporary code copied from LinearCrossing above
+        // Standard plane crossing, either first leg or second leg
       
-      if (crossingPoint.isNumericallyIdentical(edge.startPoint)) {
-        // We have to figure out if this crossing should be counted.
-        
-        // Does the crossing for this edge go up, or down?  Or can't we tell?
-        final GeoPoint[] insideIntersections = insidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
-        final GeoPoint[] outsideIntersections = outsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
-        
-        assert !(insideIntersections.length > 0 && outsideIntersections.length > 0) : "edge that ends in a crossing can't both up and down";
-        
-        if (insideIntersections.length == 0 && outsideIntersections.length == 0) {
-          return;
-        }
-
-        final boolean edgeCrossesInside = insideIntersections.length > 0;
-
-        // This depends on the previous edge that first departs from identicalness.
-        Edge assessEdge = edge;
-        GeoPoint[] assessInsideIntersections;
-        GeoPoint[] assessOutsideIntersections;
-        while (true) {
-          assessEdge = assessEdge.previous;
-          assessInsideIntersections = insidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
-          assessOutsideIntersections = outsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
-
-          assert !(assessInsideIntersections.length > 0 && assessOutsideIntersections.length > 0) : "assess edge that ends in a crossing can't both up and down";
-
-          if (assessInsideIntersections.length == 0 && assessOutsideIntersections.length == 0) {
-            continue;
-          }
-          break;
+        final Plane plane;
+        final Plane insidePlane;
+        final Plane outsidePlane;
+        final SidedPlane bound1;
+        final SidedPlane bound2;
+        if (isSecondLeg) {
+          plane = travelPlane;
+          insidePlane = travelInsidePlane;
+          outsidePlane = travelOutsidePlane;
+          bound1 = checkPointCutoffPlane;
+          bound2 = checkPointOtherCutoffPlane;
+        } else {
+          plane = testPointPlane;
+          insidePlane = testPointInsidePlane;
+          outsidePlane = testPointOutsidePlane;
+          bound1 = testPointCutoffPlane;
+          bound2 = testPointOtherCutoffPlane;
         }
         
-        // Basically, we now want to assess whether both edges that come together at this endpoint leave the plane in opposite
-        // directions.  If they do, then we should count it as a crossing; if not, we should not.  We also have to remember that
-        // each edge we look at can also be looked at again if it, too, seems to cross the plane.
-        
-        // To handle the latter situation, we need to know if the other edge will be looked at also, and then we can make
-        // a decision whether to count or not based on that.
-        
-        // Compute the crossing points of this other edge.
-        final GeoPoint[] otherCrossingPoints = plane.findCrossings(planetModel, assessEdge.plane, bound1, bound2, assessEdge.startPlane, assessEdge.endPlane);
-        
-        // Look for a matching endpoint.  If the other endpoint doesn't show up, it is either out of bounds (in which case the
-        // transition won't be counted for that edge), or it is not a crossing for that edge (so, same conclusion).
-        for (final GeoPoint otherCrossingPoint : otherCrossingPoints) {
-          if (otherCrossingPoint.isNumericallyIdentical(assessEdge.endPoint)) {
-            // Found it!
-            // Both edges will try to contribute to the crossing count.  By convention, we'll only include the earlier one.
-            // Since we're the latter point, we exit here in that case.
+        if (crossingPoint.isNumericallyIdentical(edge.startPoint)) {
+          // We have to figure out if this crossing should be counted.
+          
+          // Does the crossing for this edge go up, or down?  Or can't we tell?
+          final GeoPoint[] insideTestPointPlaneIntersections = testPointInsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane, insideTestPointCutoffPlane);
+          final GeoPoint[] insideTravelPlaneIntersections = travelInsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane, insideTravelCutoffPlane);
+          final GeoPoint[] outsideTestPointPlaneIntersections = testPointOutsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
+          final GeoPoint[] outsideTravelPlaneIntersections = travelOutsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
+          
+          assert !(insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length > 0 && outsideTestPointPlaneIntersections.length + outsideTravelPlaneIntersections.length > 0) : "edge that ends in a crossing can't both up and down";
+          
+          if (insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length == 0 && outsideTestPointPlaneIntersections.length + outsideTravelPlaneIntersections.length == 0) {
             return;
           }
-        }
-        
-        // Both edges will not count the same point, so we can proceed.  We need to determine the direction of both edges at the
-        // point where they hit the plane.  This may be complicated by the 3D geometry; it may not be safe just to look at the endpoints of the edges
-        // and make an assessment that way, since a single edge can intersect the plane at more than one point.
-        
-        final boolean assessEdgeInside = assessInsideIntersections.length > 0;
-        if (assessEdgeInside != edgeCrossesInside) {
-          crossingCount++;
-        }
-        
-      } else if (crossingPoint.isNumericallyIdentical(edge.endPoint)) {
-        // Figure out if the crossing should be counted.
-        
-        // Does the crossing for this edge go up, or down?  Or can't we tell?
-        final GeoPoint[] insideIntersections = insidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
-        final GeoPoint[] outsideIntersections = outsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
-        
-        assert !(insideIntersections.length > 0 && outsideIntersections.length > 0) : "edge that ends in a crossing can't both up and down";
-        
-        if (insideIntersections.length == 0 && outsideIntersections.length == 0) {
-          return;
-        }
-
-        final boolean edgeCrossesInside = insideIntersections.length > 0;
 
-        // This depends on the previous edge that first departs from identicalness.
-        Edge assessEdge = edge;
-        GeoPoint[] assessInsideIntersections;
-        GeoPoint[] assessOutsideIntersections;
-        while (true) {
-          assessEdge = assessEdge.next;
-          assessInsideIntersections = insidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
-          assessOutsideIntersections = outsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
-
-          assert !(assessInsideIntersections.length > 0 && assessOutsideIntersections.length > 0) : "assess edge that ends in a crossing can't both up and down";
+          final boolean edgeCrossesInside = insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length > 0;
+
+          // This depends on the previous edge that first departs from identicalness.
+          Edge assessEdge = edge;
+          GeoPoint[] assessInsideTestPointIntersections;
+          GeoPoint[] assessInsideTravelIntersections;
+          GeoPoint[] assessOutsideTestPointIntersections;
+          GeoPoint[] assessOutsideTravelIntersections;
+          while (true) {
+            assessEdge = assessEdge.previous;
+            assessInsideTestPointIntersections = testPointInsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane, insideTestPointCutoffPlane);
+            assessInsideTravelIntersections = travelInsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane, insideTravelCutoffPlane);
+            assessOutsideTestPointIntersections = testPointOutsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
+            assessOutsideTravelIntersections = travelOutsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
+
+            assert !(assessInsideTestPointIntersections.length + assessInsideTravelIntersections.length > 0 && assessOutsideTestPointIntersections.length + assessOutsideTravelIntersections.length > 0) : "assess edge that ends in a crossing can't both up and down";
+
+            if (assessInsideTestPointIntersections.length + assessInsideTravelIntersections.length == 0 && assessOutsideTestPointIntersections.length + assessOutsideTravelIntersections.length == 0) {
+              continue;
+            }
+            break;
+          }
 
-          if (assessInsideIntersections.length == 0 && assessOutsideIntersections.length == 0) {
-            continue;
+          // Basically, we now want to assess whether both edges that come together at this endpoint leave the plane in opposite
+          // directions.  If they do, then we should count it as a crossing; if not, we should not.  We also have to remember that
+          // each edge we look at can also be looked at again if it, too, seems to cross the plane.
+          
+          // To handle the latter situation, we need to know if the other edge will be looked at also, and then we can make
+          // a decision whether to count or not based on that.
+          
+          // Compute the crossing points of this other edge.
+          final GeoPoint[] otherCrossingPoints = plane.findCrossings(planetModel, assessEdge.plane, bound1, bound2, assessEdge.startPlane, assessEdge.endPlane);
+          
+          // Look for a matching endpoint.  If the other endpoint doesn't show up, it is either out of bounds (in which case the
+          // transition won't be counted for that edge), or it is not a crossing for that edge (so, same conclusion).
+          for (final GeoPoint otherCrossingPoint : otherCrossingPoints) {
+            if (otherCrossingPoint.isNumericallyIdentical(assessEdge.endPoint)) {
+              // Found it!
+              // Both edges will try to contribute to the crossing count.  By convention, we'll only include the earlier one.
+              // Since we're the latter point, we exit here in that case.
+              return;
+            }
+          }
+          
+          // Both edges will not count the same point, so we can proceed.  We need to determine the direction of both edges at the
+          // point where they hit the plane.  This may be complicated by the 3D geometry; it may not be safe just to look at the endpoints of the edges
+          // and make an assessment that way, since a single edge can intersect the plane at more than one point.
+          
+          final boolean assessEdgeInside = assessInsideTestPointIntersections.length + assessInsideTravelIntersections.length > 0;
+          if (assessEdgeInside != edgeCrossesInside) {
+            crossingCount++;
+          }
+          
+        } else if (crossingPoint.isNumericallyIdentical(edge.endPoint)) {
+          // Figure out if the crossing should be counted.
+          
+          // Does the crossing for this edge go up, or down?  Or can't we tell?
+          final GeoPoint[] insideTestPointPlaneIntersections = testPointInsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane, insideTestPointCutoffPlane);
+          final GeoPoint[] insideTravelPlaneIntersections = travelInsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane, insideTravelCutoffPlane);
+          final GeoPoint[] outsideTestPointPlaneIntersections = testPointOutsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
+          final GeoPoint[] outsideTravelPlaneIntersections = travelOutsidePlane.findIntersections(planetModel, edge.plane, edge.startPlane, edge.endPlane);
+          
+          assert !(insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length > 0 && outsideTestPointPlaneIntersections.length + outsideTravelPlaneIntersections.length > 0) : "edge that ends in a crossing can't both up and down";
+          
+          if (insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length == 0 && outsideTestPointPlaneIntersections.length + outsideTravelPlaneIntersections.length == 0) {
+            return;
           }
-          break;
-        }
-        
-        // Basically, we now want to assess whether both edges that come together at this endpoint leave the plane in opposite
-        // directions.  If they do, then we should count it as a crossing; if not, we should not.  We also have to remember that
-        // each edge we look at can also be looked at again if it, too, seems to cross the plane.
-        
-        // By definition, we're the earlier plane in this case, so any crossing we detect we must count, by convention.  It is unnecessary
-        // to consider what the other edge does, because when we get to it, it will look back and figure out what we did for this one.
-        
-        // We need to determine the direction of both edges at the
-        // point where they hit the plane.  This may be complicated by the 3D geometry; it may not be safe just to look at the endpoints of the edges
-        // and make an assessment that way, since a single edge can intersect the plane at more than one point.
 
-        final boolean assessEdgeInside = assessInsideIntersections.length > 0;
-        if (assessEdgeInside != edgeCrossesInside) {
+          final boolean edgeCrossesInside = insideTestPointPlaneIntersections.length + insideTravelPlaneIntersections.length > 0;
+
+          // This depends on the previous edge that first departs from identicalness.
+          Edge assessEdge = edge;
+          GeoPoint[] assessInsideTestPointIntersections;
+          GeoPoint[] assessInsideTravelIntersections;
+          GeoPoint[] assessOutsideTestPointIntersections;
+          GeoPoint[] assessOutsideTravelIntersections;
+          while (true) {
+            assessEdge = assessEdge.next;
+            assessInsideTestPointIntersections = testPointInsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane, insideTestPointCutoffPlane);
+            assessInsideTravelIntersections = travelInsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane, insideTravelCutoffPlane);
+            assessOutsideTestPointIntersections = testPointOutsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
+            assessOutsideTravelIntersections = travelOutsidePlane.findIntersections(planetModel, assessEdge.plane, assessEdge.startPlane, assessEdge.endPlane);
+
+            assert !(assessInsideTestPointIntersections.length + assessInsideTravelIntersections.length > 0 && assessOutsideTestPointIntersections.length + assessOutsideTravelIntersections.length > 0) : "assess edge that ends in a crossing can't both up and down";
+
+            if (assessInsideTestPointIntersections.length + assessInsideTravelIntersections.length == 0 && assessOutsideTestPointIntersections.length + assessOutsideTravelIntersections.length == 0) {
+              continue;
+            }
+            break;
+          }
+          
+          // Basically, we now want to assess whether both edges that come together at this endpoint leave the plane in opposite
+          // directions.  If they do, then we should count it as a crossing; if not, we should not.  We also have to remember that
+          // each edge we look at can also be looked at again if it, too, seems to cross the plane.
+          
+          // By definition, we're the earlier plane in this case, so any crossing we detect we must count, by convention.  It is unnecessary
+          // to consider what the other edge does, because when we get to it, it will look back and figure out what we did for this one.
+          
+          // We need to determine the direction of both edges at the
+          // point where they hit the plane.  This may be complicated by the 3D geometry; it may not be safe just to look at the endpoints of the edges
+          // and make an assessment that way, since a single edge can intersect the plane at more than one point.
+
+          final boolean assessEdgeInside = assessInsideTestPointIntersections.length + assessInsideTravelIntersections.length > 0;
+          if (assessEdgeInside != edgeCrossesInside) {
+            crossingCount++;
+          }
+        } else {
+          // Not a special case, so we can safely count a crossing.
           crossingCount++;
         }
-
-      } else {
-        crossingCount++;
       }
     }
   }