I will remove this branch.

Let's debate on the issue instead:
https://issues.apache.org/jira/browse/LUCENE-7157

Mike McCandless

http://blog.mikemccandless.com


On Sun, Apr 3, 2016 at 9:08 AM, Robert Muir <rcm...@gmail.com> wrote:
> I'm not going to argue with you about why GeoConcavePolygon.java
> should not be public.
>
> Instead, you can convince me why it should be. public should not be the 
> default.
>
> On Sun, Apr 3, 2016 at 8:56 AM, Karl Wright <daddy...@gmail.com> wrote:
>>>>>>>>
>> No, I responded to the commit, because I already said on the ticket "i
>> don't think a user should have to know about concaveness", and then I
>> see GeoConcavePolygon.java added in this commit!!!!!!!!
>> <<<<<<
>>
>> The API that you helped define uses point clockwise/counter-clockwise
>> ordering to determine what side of the polygon edge is the "inside" of the
>> polygon.  I do not see how you can finesse this.
>>
>> Users use GeoPolygonFactory.makeGeoPolygon() to construct complex polygons.
>> They don't need to know about convexity or concavity.  They only need to
>> know about point ordering.
>>
>> So I fail to understand your point?
>>
>> Karl
>>
>>
>>
>> <<<<<<
>>
>>
>>
>> On Sun, Apr 3, 2016 at 8:48 AM, Robert Muir <rcm...@gmail.com> wrote:
>>>
>>> No, I responded to the commit, because I already said on the ticket "i
>>> don't think a user should have to know about concaveness", and then I
>>> see GeoConcavePolygon.java added in this commit!!!!!!!!
>>>
>>>
>>>
>>> On Sun, Apr 3, 2016 at 8:40 AM, Karl Wright <daddy...@gmail.com> wrote:
>>> > Hi Robert,
>>> >
>>> > In case you are unaware, I am not a committer.  I just uploaded the
>>> > patch.
>>> > Mike created a branch to allow us to collaborate more easily.  I do not
>>> > think anything has hit trunk -- but if it did, I'm unaware of it.
>>> >
>>> > Do you want me to remove the patch from the ticket?
>>> >
>>> > Karl
>>> >
>>> >
>>> > On Sun, Apr 3, 2016 at 8:37 AM, Robert Muir <rcm...@gmail.com> wrote:
>>> >>
>>> >> My concern has everything to do with the ticket. Please back the change
>>> >> out.
>>> >>
>>> >> We are an API and so adding a public class is a very serious thing.
>>> >>
>>> >> On Sun, Apr 3, 2016 at 8:14 AM, Karl Wright <daddy...@gmail.com> wrote:
>>> >> > Robert,
>>> >> >
>>> >> > Your concern is noted but that has nothing really to do with this
>>> >> > ticket.
>>> >> > Would you mind creating another ticket for re-engineering Geo3D so
>>> >> > that
>>> >> > it
>>> >> > has fewer public classes?  Doing things piecemeal as part of another
>>> >> > ticket,
>>> >> > especially one as complex as this one, does not seem like the right
>>> >> > way
>>> >> > to
>>> >> > go.
>>> >> >
>>> >> > Karl
>>> >> >
>>> >> >
>>> >> > On Sun, Apr 3, 2016 at 8:09 AM, Robert Muir <rcm...@gmail.com> wrote:
>>> >> >>
>>> >> >> Why does this change need to add so many public classes? I already
>>> >> >> mentioned on the issue: the user need not concern themselves with
>>> >> >> these impl details.
>>> >> >>
>>> >> >> It is frustrating that this package has 60 public classes (and if
>>> >> >> everything is public, why the hell does it need factories?). Can we
>>> >> >> please make these classes package private? If this cannot be done in
>>> >> >> an obvious way, then can you please back out the commit and we do
>>> >> >> whatever redesign is necessary. Certainly, I think we can all agree
>>> >> >> that having both public factories and making the things they create
>>> >> >> public too (which all of this is impl detail) is completely bogus.
>>> >> >>
>>> >> >> I know that I will frustrate you with this response, but we have to
>>> >> >> stop the patient from bleeding or it will never get better.
>>> >> >>
>>> >> >> On Sat, Apr 2, 2016 at 5:58 PM,  <mikemcc...@apache.org> wrote:
>>> >> >> > Repository: lucene-solr
>>> >> >> > Updated Branches:
>>> >> >> >   refs/heads/lucene-7157 [created] 98e04cf49
>>> >> >> >
>>> >> >> >
>>> >> >> > LUCENE_7157: current patch
>>> >> >> >
>>> >> >> >
>>> >> >> > Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
>>> >> >> > Commit:
>>> >> >> > http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/98e04cf4
>>> >> >> > Tree:
>>> >> >> > http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/98e04cf4
>>> >> >> > Diff:
>>> >> >> > http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/98e04cf4
>>> >> >> >
>>> >> >> > Branch: refs/heads/lucene-7157
>>> >> >> > Commit: 98e04cf49c7a4d5a551679792da7c3fa9559013f
>>> >> >> > Parents: 9ed95bc
>>> >> >> > Author: Mike McCandless <mikemcc...@apache.org>
>>> >> >> > Authored: Sat Apr 2 18:00:43 2016 -0400
>>> >> >> > Committer: Mike McCandless <mikemcc...@apache.org>
>>> >> >> > Committed: Sat Apr 2 18:00:43 2016 -0400
>>> >> >> >
>>> >> >> >
>>> >> >> >
>>> >> >> > ----------------------------------------------------------------------
>>> >> >> >  .../org/apache/lucene/spatial3d/Geo3DPoint.java |   8 +-
>>> >> >> >  .../spatial3d/geom/GeoConcavePolygon.java       | 380 ++++++++
>>> >> >> >  .../lucene/spatial3d/geom/GeoConvexPolygon.java | 162 +++-
>>> >> >> >  .../spatial3d/geom/GeoPolygonFactory.java       | 857
>>> >> >> > ++++++++++++++++---
>>> >> >> >  .../lucene/spatial3d/geom/SidedPlane.java       |  20 +-
>>> >> >> >  .../lucene/spatial3d/geom/GeoPolygonTest.java   |  78 +-
>>> >> >> >  6 files changed, 1355 insertions(+), 150 deletions(-)
>>> >> >> >
>>> >> >> >
>>> >> >> > ----------------------------------------------------------------------
>>> >> >> >
>>> >> >> >
>>> >> >> >
>>> >> >> >
>>> >> >> >
>>> >> >> > http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/98e04cf4/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/Geo3DPoint.java
>>> >> >> >
>>> >> >> >
>>> >> >> > ----------------------------------------------------------------------
>>> >> >> > diff --git
>>> >> >> >
>>> >> >> >
>>> >> >> > a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/Geo3DPoint.java
>>> >> >> >
>>> >> >> >
>>> >> >> > b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/Geo3DPoint.java
>>> >> >> > index 45e17b7..f0f2020 100644
>>> >> >> > ---
>>> >> >> >
>>> >> >> >
>>> >> >> > a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/Geo3DPoint.java
>>> >> >> > +++
>>> >> >> >
>>> >> >> >
>>> >> >> > b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/Geo3DPoint.java
>>> >> >> > @@ -22,6 +22,7 @@ import java.util.ArrayList;
>>> >> >> >  import org.apache.lucene.document.Field;
>>> >> >> >  import org.apache.lucene.document.FieldType;
>>> >> >> >  import org.apache.lucene.index.PointValues;
>>> >> >> > +import org.apache.lucene.spatial3d.geom.Vector;
>>> >> >> >  import org.apache.lucene.spatial3d.geom.GeoPoint;
>>> >> >> >  import org.apache.lucene.spatial3d.geom.GeoShape;
>>> >> >> >  import org.apache.lucene.spatial3d.geom.PlanetModel;
>>> >> >> > @@ -149,11 +150,8 @@ public final class Geo3DPoint extends Field {
>>> >> >> >        checkLongitude(longitude);
>>> >> >> >        polyPoints.add(new GeoPoint(PlanetModel.WGS84,
>>> >> >> > fromDegrees(latitude), fromDegrees(longitude)));
>>> >> >> >      }
>>> >> >> > -    // We don't know what the sense of the polygon is without
>>> >> >> > providing
>>> >> >> > the index of one vertex we know to be convex.
>>> >> >> > -    // Since that doesn't fit with the "super-simple API"
>>> >> >> > requirements,
>>> >> >> > we just use the index of the first one, and people have to just
>>> >> >> > -    // know to do it that way.
>>> >> >> > -    final int convexPointIndex = 0;
>>> >> >> > -    final GeoShape shape =
>>> >> >> > GeoPolygonFactory.makeGeoPolygon(PlanetModel.WGS84, polyPoints,
>>> >> >> > convexPointIndex);
>>> >> >> > +    // We use the polygon constructor that looks at point order.
>>> >> >> > +    final GeoShape shape =
>>> >> >> > GeoPolygonFactory.makeGeoPolygon(PlanetModel.WGS84, polyPoints,
>>> >> >> > null);
>>> >> >> >      return newShapeQuery(field, shape);
>>> >> >> >    }
>>> >> >> >
>>> >> >> >
>>> >> >> >
>>> >> >> >
>>> >> >> >
>>> >> >> > http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/98e04cf4/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoConcavePolygon.java
>>> >> >> >
>>> >> >> >
>>> >> >> > ----------------------------------------------------------------------
>>> >> >> > diff --git
>>> >> >> >
>>> >> >> >
>>> >> >> > a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoConcavePolygon.java
>>> >> >> >
>>> >> >> >
>>> >> >> > b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoConcavePolygon.java
>>> >> >> > new file mode 100644
>>> >> >> > index 0000000..9c60f69
>>> >> >> > --- /dev/null
>>> >> >> > +++
>>> >> >> >
>>> >> >> >
>>> >> >> > b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoConcavePolygon.java
>>> >> >> > @@ -0,0 +1,380 @@
>>> >> >> > +/*
>>> >> >> > + * 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;
>>> >> >> > +
>>> >> >> > +/**
>>> >> >> > + * GeoConcavePolygon objects are generic building blocks of more
>>> >> >> > complex structures.
>>> >> >> > + * The only restrictions on these objects are: (1) they must be
>>> >> >> > concave; (2) they must have
>>> >> >> > + * a maximum extent larger than PI.  Violating either one of
>>> >> >> > these
>>> >> >> > limits will
>>> >> >> > + * cause the logic to fail.
>>> >> >> > + *
>>> >> >> > + * @lucene.experimental
>>> >> >> > + */
>>> >> >> > +public class GeoConcavePolygon extends GeoBasePolygon {
>>> >> >> > +  /** The list of polygon points */
>>> >> >> > +  protected final List<GeoPoint> points;
>>> >> >> > +  /** A bitset describing, for each edge, whether it is internal
>>> >> >> > or
>>> >> >> > not
>>> >> >> > */
>>> >> >> > +  protected final BitSet isInternalEdges;
>>> >> >> > +  /** The list of holes.  If a point is in the hole, it is *not*
>>> >> >> > in
>>> >> >> > the
>>> >> >> > polygon */
>>> >> >> > +  protected final List<GeoPolygon> holes;
>>> >> >> > +
>>> >> >> > +  /** A list of edges */
>>> >> >> > +  protected SidedPlane[] edges = null;
>>> >> >> > +  /** A list of inverted edges */
>>> >> >> > +  protected SidedPlane[] invertedEdges = null;
>>> >> >> > +  /** The set of notable points for each edge */
>>> >> >> > +  protected GeoPoint[][] notableEdgePoints = null;
>>> >> >> > +  /** A point which is on the boundary of the polygon */
>>> >> >> > +  protected GeoPoint[] edgePoints = null;
>>> >> >> > +  /** Tracking the maximum distance we go at any one time, so to
>>> >> >> > be
>>> >> >> > sure it's legal */
>>> >> >> > +  protected double fullDistance = 0.0;
>>> >> >> > +  /** Set to true when the polygon is complete */
>>> >> >> > +  protected boolean isDone = false;
>>> >> >> > +
>>> >> >> > +  protected Membership eitherBound = new EitherBound();
>>> >> >> > +
>>> >> >> > +  /**
>>> >> >> > +   * Create a concave polygon from a list of points.  The first
>>> >> >> > point
>>> >> >> > must be on the
>>> >> >> > +   * external edge.
>>> >> >> > +   *@param planetModel is the planet model.
>>> >> >> > +   *@param pointList is the list of points to create the polygon
>>> >> >> > from.
>>> >> >> > +   */
>>> >> >> > +  public GeoConcavePolygon(final PlanetModel planetModel, final
>>> >> >> > List<GeoPoint> pointList) {
>>> >> >> > +    this(planetModel, pointList, null);
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  /**
>>> >> >> > +   * Create a concave polygon from a list of points.  The first
>>> >> >> > point
>>> >> >> > must be on the
>>> >> >> > +   * external edge.
>>> >> >> > +   *@param planetModel is the planet model.
>>> >> >> > +   *@param pointList is the list of points to create the polygon
>>> >> >> > from.
>>> >> >> > +   *@param holes is the list of GeoPolygon objects that describe
>>> >> >> > holes
>>> >> >> > in the concave polygon.  Null == no holes.
>>> >> >> > +   */
>>> >> >> > +  public GeoConcavePolygon(final PlanetModel planetModel, final
>>> >> >> > List<GeoPoint> pointList, final List<GeoPolygon> holes) {
>>> >> >> > +    super(planetModel);
>>> >> >> > +    this.points = pointList;
>>> >> >> > +    this.holes = holes;
>>> >> >> > +    this.isInternalEdges = new BitSet();
>>> >> >> > +    done(false);
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  /**
>>> >> >> > +   * Create a concave polygon from a list of points, keeping
>>> >> >> > track
>>> >> >> > of
>>> >> >> > which boundaries
>>> >> >> > +   * are internal.  This is used when creating a polygon as a
>>> >> >> > building
>>> >> >> > block for another shape.
>>> >> >> > +   *@param planetModel is the planet model.
>>> >> >> > +   *@param pointList is the set of points to create the polygon
>>> >> >> > from.
>>> >> >> > +   *@param internalEdgeFlags is a bitset describing whether each
>>> >> >> > edge
>>> >> >> > is internal or not.
>>> >> >> > +   *@param returnEdgeInternal is true when the final return edge
>>> >> >> > is
>>> >> >> > an
>>> >> >> > internal one.
>>> >> >> > +   */
>>> >> >> > +  public GeoConcavePolygon(final PlanetModel planetModel,
>>> >> >> > +    final List<GeoPoint> pointList,
>>> >> >> > +    final BitSet internalEdgeFlags,
>>> >> >> > +    final boolean returnEdgeInternal) {
>>> >> >> > +    this(planetModel, pointList, null, internalEdgeFlags,
>>> >> >> > returnEdgeInternal);
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  /**
>>> >> >> > +   * Create a concave polygon from a list of points, keeping
>>> >> >> > track
>>> >> >> > of
>>> >> >> > which boundaries
>>> >> >> > +   * are internal.  This is used when creating a polygon as a
>>> >> >> > building
>>> >> >> > block for another shape.
>>> >> >> > +   *@param planetModel is the planet model.
>>> >> >> > +   *@param pointList is the set of points to create the polygon
>>> >> >> > from.
>>> >> >> > +   *@param holes is the list of GeoPolygon objects that describe
>>> >> >> > holes
>>> >> >> > in the concave polygon.  Null == no holes.
>>> >> >> > +   *@param internalEdgeFlags is a bitset describing whether each
>>> >> >> > edge
>>> >> >> > is internal or not.
>>> >> >> > +   *@param returnEdgeInternal is true when the final return edge
>>> >> >> > is
>>> >> >> > an
>>> >> >> > internal one.
>>> >> >> > +   */
>>> >> >> > +  public GeoConcavePolygon(final PlanetModel planetModel,
>>> >> >> > +    final List<GeoPoint> pointList,
>>> >> >> > +    final List<GeoPolygon> holes,
>>> >> >> > +    final BitSet internalEdgeFlags,
>>> >> >> > +    final boolean returnEdgeInternal) {
>>> >> >> > +    super(planetModel);
>>> >> >> > +    this.points = pointList;
>>> >> >> > +    this.holes = holes;
>>> >> >> > +    this.isInternalEdges = internalEdgeFlags;
>>> >> >> > +    done(returnEdgeInternal);
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  /**
>>> >> >> > +   * Create a concave polygon, with a starting latitude and
>>> >> >> > longitude.
>>> >> >> > +   * Accepts only values in the following ranges: lat: {@code
>>> >> >> > -PI/2
>>> >> >> > ->
>>> >> >> > PI/2}, lon: {@code -PI -> PI}
>>> >> >> > +   *@param planetModel is the planet model.
>>> >> >> > +   *@param startLatitude is the latitude of the first point.
>>> >> >> > +   *@param startLongitude is the longitude of the first point.
>>> >> >> > +   */
>>> >> >> > +  public GeoConcavePolygon(final PlanetModel planetModel,
>>> >> >> > +    final double startLatitude,
>>> >> >> > +    final double startLongitude) {
>>> >> >> > +    this(planetModel, startLatitude, startLongitude, null);
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  /**
>>> >> >> > +   * Create a concave polygon, with a starting latitude and
>>> >> >> > longitude.
>>> >> >> > +   * Accepts only values in the following ranges: lat: {@code
>>> >> >> > -PI/2
>>> >> >> > ->
>>> >> >> > PI/2}, lon: {@code -PI -> PI}
>>> >> >> > +   *@param planetModel is the planet model.
>>> >> >> > +   *@param startLatitude is the latitude of the first point.
>>> >> >> > +   *@param startLongitude is the longitude of the first point.
>>> >> >> > +   *@param holes is the list of GeoPolygon objects that describe
>>> >> >> > holes
>>> >> >> > in the concave polygon.  Null == no holes.
>>> >> >> > +   */
>>> >> >> > +  public GeoConcavePolygon(final PlanetModel planetModel,
>>> >> >> > +    final double startLatitude,
>>> >> >> > +    final double startLongitude,
>>> >> >> > +    final List<GeoPolygon> holes) {
>>> >> >> > +    super(planetModel);
>>> >> >> > +    points = new ArrayList<>();
>>> >> >> > +    this.holes = holes;
>>> >> >> > +    isInternalEdges = new BitSet();
>>> >> >> > +    points.add(new GeoPoint(planetModel, startLatitude,
>>> >> >> > startLongitude));
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  /**
>>> >> >> > +   * Add a point to the polygon.
>>> >> >> > +   * Accepts only values in the following ranges: lat: {@code
>>> >> >> > -PI/2
>>> >> >> > ->
>>> >> >> > PI/2}, lon: {@code -PI -> PI}
>>> >> >> > +   *
>>> >> >> > +   * @param latitude       is the latitude of the next point.
>>> >> >> > +   * @param longitude      is the longitude of the next point.
>>> >> >> > +   * @param isInternalEdge is true if the edge just added with
>>> >> >> > this
>>> >> >> > point should be considered "internal", and not
>>> >> >> > +   *                       intersected as part of the
>>> >> >> > intersects()
>>> >> >> > operation.
>>> >> >> > +   */
>>> >> >> > +  public void addPoint(final double latitude, final double
>>> >> >> > longitude,
>>> >> >> > final boolean isInternalEdge) {
>>> >> >> > +    if (isDone)
>>> >> >> > +      throw new IllegalStateException("Can't call addPoint() if
>>> >> >> > done()
>>> >> >> > already called");
>>> >> >> > +    if (isInternalEdge)
>>> >> >> > +      isInternalEdges.set(points.size() - 1);
>>> >> >> > +    points.add(new GeoPoint(planetModel, latitude, longitude));
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  /**
>>> >> >> > +   * Finish the polygon, by connecting the last added point with
>>> >> >> > the
>>> >> >> > starting point.
>>> >> >> > +   *@param isInternalReturnEdge is true if the return edge (back
>>> >> >> > to
>>> >> >> > start) is an internal one.
>>> >> >> > +   */
>>> >> >> > +  public void done(final boolean isInternalReturnEdge) {
>>> >> >> > +    if (isDone)
>>> >> >> > +      throw new IllegalStateException("Can't call done() more
>>> >> >> > than
>>> >> >> > once");
>>> >> >> > +    // If fewer than 3 points, can't do it.
>>> >> >> > +    if (points.size() < 3)
>>> >> >> > +      throw new IllegalArgumentException("Polygon needs at least
>>> >> >> > three
>>> >> >> > points.");
>>> >> >> > +
>>> >> >> > +    if (isInternalReturnEdge)
>>> >> >> > +      isInternalEdges.set(points.size() - 1);
>>> >> >> > +
>>> >> >> > +    isDone = true;
>>> >> >> > +
>>> >> >> > +    // Time to construct the planes.  If the polygon is truly
>>> >> >> > concave
>>> >> >> > then any adjacent point
>>> >> >> > +    // to a segment can provide an exterior measurement.  Note:
>>> >> >> > We
>>> >> >> > build the true planes
>>> >> >> > +    // here and use the logic to return what *isn't* inside all
>>> >> >> > of
>>> >> >> > them.
>>> >> >> > +    edges = new SidedPlane[points.size()];
>>> >> >> > +    invertedEdges = new SidedPlane[points.size()];
>>> >> >> > +    notableEdgePoints = new GeoPoint[points.size()][];
>>> >> >> > +
>>> >> >> > +    for (int i = 0; i < points.size(); i++) {
>>> >> >> > +      final GeoPoint start = points.get(i);
>>> >> >> > +      final GeoPoint end = points.get(legalIndex(i + 1));
>>> >> >> > +      final double distance = start.arcDistance(end);
>>> >> >> > +      if (distance > fullDistance)
>>> >> >> > +        fullDistance = distance;
>>> >> >> > +      final GeoPoint check = points.get(legalIndex(i + 2));
>>> >> >> > +      // Here note the flip of the sense of the sided plane!!
>>> >> >> > +      final SidedPlane sp = new SidedPlane(check, false, start,
>>> >> >> > end);
>>> >> >> > +      //System.out.println("Created edge "+sp+" using
>>> >> >> > start="+start+"
>>> >> >> > end="+end+" check="+check);
>>> >> >> > +      edges[i] = sp;
>>> >> >> > +      invertedEdges[i] = new SidedPlane(sp);
>>> >> >> > +      notableEdgePoints[i] = new GeoPoint[]{start, end};
>>> >> >> > +    }
>>> >> >> > +    // In order to naively confirm that the polygon is concave, I
>>> >> >> > would
>>> >> >> > need to
>>> >> >> > +    // check every edge, and verify that every point (other than
>>> >> >> > the
>>> >> >> > edge endpoints)
>>> >> >> > +    // is within the edge's sided plane.  This is an order n^2
>>> >> >> > operation.  That's still
>>> >> >> > +    // not wrong, though, because everything else about polygons
>>> >> >> > has
>>> >> >> > a
>>> >> >> > similar cost.
>>> >> >> > +    for (int edgeIndex = 0; edgeIndex < edges.length;
>>> >> >> > edgeIndex++) {
>>> >> >> > +      final SidedPlane edge = edges[edgeIndex];
>>> >> >> > +      for (int pointIndex = 0; pointIndex < points.size();
>>> >> >> > pointIndex++) {
>>> >> >> > +        if (pointIndex != edgeIndex && pointIndex !=
>>> >> >> > legalIndex(edgeIndex + 1)) {
>>> >> >> > +          if (edge.isWithin(points.get(pointIndex)))
>>> >> >> > +            throw new IllegalArgumentException("Polygon is not
>>> >> >> > concave:
>>> >> >> > Point " + points.get(pointIndex) + " Edge " + edge);
>>> >> >> > +        }
>>> >> >> > +      }
>>> >> >> > +    }
>>> >> >> > +    // Pick an edge point arbitrarily
>>> >> >> > +    edgePoints = new GeoPoint[]{points.get(0)};
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  /** 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();
>>> >> >> > +    return index;
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  @Override
>>> >> >> > +  public boolean isWithin(final double x, final double y, final
>>> >> >> > double
>>> >> >> > z) {
>>> >> >> > +    // If present within *any* plane, then it is a member, except
>>> >> >> > where
>>> >> >> > there are holes.
>>> >> >> > +    boolean isMember = false;
>>> >> >> > +    for (final SidedPlane edge : edges) {
>>> >> >> > +      if (edge.isWithin(x, y, z)) {
>>> >> >> > +        isMember = true;
>>> >> >> > +        break;
>>> >> >> > +      }
>>> >> >> > +    }
>>> >> >> > +    if (isMember == false) {
>>> >> >> > +      return false;
>>> >> >> > +    }
>>> >> >> > +    if (holes != null) {
>>> >> >> > +      for (final GeoPolygon polygon : holes) {
>>> >> >> > +        if (polygon.isWithin(x, y, z)) {
>>> >> >> > +          return false;
>>> >> >> > +        }
>>> >> >> > +      }
>>> >> >> > +    }
>>> >> >> > +    return true;
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  @Override
>>> >> >> > +  public GeoPoint[] getEdgePoints() {
>>> >> >> > +    return edgePoints;
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  @Override
>>> >> >> > +  public boolean intersects(final Plane p, final GeoPoint[]
>>> >> >> > notablePoints, final Membership... bounds) {
>>> >> >> > +    // The bounding planes are inverted and complementary.  For
>>> >> >> > intersection computation, we
>>> >> >> > +    // cannot use them as bounds.  They are independent
>>> >> >> > hemispheres.
>>> >> >> > +    for (int edgeIndex = 0; edgeIndex < edges.length;
>>> >> >> > edgeIndex++) {
>>> >> >> > +      final SidedPlane edge = edges[edgeIndex];
>>> >> >> > +      final GeoPoint[] points =
>>> >> >> > this.notableEdgePoints[edgeIndex];
>>> >> >> > +      if (!isInternalEdges.get(edgeIndex)) {
>>> >> >> > +        if (edge.intersects(planetModel, p, notablePoints,
>>> >> >> > points,
>>> >> >> > bounds, eitherBound)) {
>>> >> >> > +          //System.err.println(" intersects!");
>>> >> >> > +          return true;
>>> >> >> > +        }
>>> >> >> > +      }
>>> >> >> > +    }
>>> >> >> > +    if (holes != null) {
>>> >> >> > +      // Each hole needs to be looked at for intersection too,
>>> >> >> > since
>>> >> >> > a
>>> >> >> > shape can be entirely within the hole
>>> >> >> > +      for (final GeoPolygon hole : holes) {
>>> >> >> > +        if (hole.intersects(p, notablePoints, bounds)) {
>>> >> >> > +          return true;
>>> >> >> > +        }
>>> >> >> > +      }
>>> >> >> > +    }
>>> >> >> > +    //System.err.println(" no intersection");
>>> >> >> > +    return false;
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  /** A membership implementation representing a wide bound where
>>> >> >> > planes are complementary.
>>> >> >> > +   */
>>> >> >> > +  protected class EitherBound implements Membership {
>>> >> >> > +    /** Constructor.
>>> >> >> > +      */
>>> >> >> > +    public EitherBound() {
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    @Override
>>> >> >> > +    public boolean isWithin(final Vector v) {
>>> >> >> > +      for (final SidedPlane edge : invertedEdges) {
>>> >> >> > +        if (!edge.isWithin(v)) {
>>> >> >> > +          return false;
>>> >> >> > +        }
>>> >> >> > +      }
>>> >> >> > +      return true;
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    @Override
>>> >> >> > +    public boolean isWithin(final double x, final double y, final
>>> >> >> > double z) {
>>> >> >> > +      for (final SidedPlane edge : invertedEdges) {
>>> >> >> > +        if (!edge.isWithin(x, y, z)) {
>>> >> >> > +          return false;
>>> >> >> > +        }
>>> >> >> > +      }
>>> >> >> > +      return true;
>>> >> >> > +    }
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  @Override
>>> >> >> > +  public void getBounds(Bounds bounds) {
>>> >> >> > +    super.getBounds(bounds);
>>> >> >> > +    bounds.isWide();
>>> >> >> > +
>>> >> >> > +    // Add all the points
>>> >> >> > +    for (final GeoPoint point : points) {
>>> >> >> > +      bounds.addPoint(point);
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    // Add planes with membership.
>>> >> >> > +    for (final SidedPlane edge : edges) {
>>> >> >> > +      bounds.addPlane(planetModel, edge, eitherBound);
>>> >> >> > +    }
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  @Override
>>> >> >> > +  protected double outsideDistance(final DistanceStyle
>>> >> >> > distanceStyle,
>>> >> >> > final double x, final double y, final double z) {
>>> >> >> > +    double minimumDistance = Double.MAX_VALUE;
>>> >> >> > +    for (final GeoPoint edgePoint : points) {
>>> >> >> > +      final double newDist =
>>> >> >> > distanceStyle.computeDistance(edgePoint,
>>> >> >> > x,y,z);
>>> >> >> > +      if (newDist < minimumDistance) {
>>> >> >> > +        minimumDistance = newDist;
>>> >> >> > +      }
>>> >> >> > +    }
>>> >> >> > +    for (final Plane edgePlane : edges) {
>>> >> >> > +      final double newDist =
>>> >> >> > distanceStyle.computeDistance(planetModel,
>>> >> >> > edgePlane, x, y, z, eitherBound);
>>> >> >> > +      if (newDist < minimumDistance) {
>>> >> >> > +        minimumDistance = newDist;
>>> >> >> > +      }
>>> >> >> > +    }
>>> >> >> > +    return minimumDistance;
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  @Override
>>> >> >> > +  public boolean equals(Object o) {
>>> >> >> > +    if (!(o instanceof GeoConcavePolygon))
>>> >> >> > +      return false;
>>> >> >> > +    GeoConcavePolygon other = (GeoConcavePolygon) o;
>>> >> >> > +    if (!super.equals(other))
>>> >> >> > +      return false;
>>> >> >> > +    if (!other.isInternalEdges.equals(isInternalEdges))
>>> >> >> > +      return false;
>>> >> >> > +    if (other.holes != null || holes != null) {
>>> >> >> > +      if (other.holes == null || holes == null) {
>>> >> >> > +        return false;
>>> >> >> > +      }
>>> >> >> > +      if (!other.holes.equals(holes)) {
>>> >> >> > +        return false;
>>> >> >> > +      }
>>> >> >> > +    }
>>> >> >> > +    return (other.points.equals(points));
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  @Override
>>> >> >> > +  public int hashCode() {
>>> >> >> > +    int result = super.hashCode();
>>> >> >> > +    result = 31 * result + points.hashCode();
>>> >> >> > +    if (holes != null) {
>>> >> >> > +      result = 31 * result + holes.hashCode();
>>> >> >> > +    }
>>> >> >> > +    return result;
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  @Override
>>> >> >> > +  public String toString() {
>>> >> >> > +    return "GeoConcavePolygon: {planetmodel=" + planetModel + ",
>>> >> >> > points=" + points + ((holes== null)?"":", holes=" + holes) + "}";
>>> >> >> > +  }
>>> >> >> > +}
>>> >> >> > +
>>> >> >> >
>>> >> >> >
>>> >> >> >
>>> >> >> >
>>> >> >> > http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/98e04cf4/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoConvexPolygon.java
>>> >> >> >
>>> >> >> >
>>> >> >> > ----------------------------------------------------------------------
>>> >> >> > diff --git
>>> >> >> >
>>> >> >> >
>>> >> >> > a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoConvexPolygon.java
>>> >> >> >
>>> >> >> >
>>> >> >> > b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoConvexPolygon.java
>>> >> >> > index fb024b6..a03c50a 100755
>>> >> >> > ---
>>> >> >> >
>>> >> >> >
>>> >> >> > a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoConvexPolygon.java
>>> >> >> > +++
>>> >> >> >
>>> >> >> >
>>> >> >> > b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoConvexPolygon.java
>>> >> >> > @@ -33,6 +33,8 @@ public class GeoConvexPolygon extends
>>> >> >> > GeoBasePolygon {
>>> >> >> >    protected final List<GeoPoint> points;
>>> >> >> >    /** A bitset describing, for each edge, whether it is internal
>>> >> >> > or
>>> >> >> > not
>>> >> >> > */
>>> >> >> >    protected final BitSet isInternalEdges;
>>> >> >> > +  /** The list of holes.  If a point is in the hole, it is *not*
>>> >> >> > in
>>> >> >> > the
>>> >> >> > polygon */
>>> >> >> > +  protected final List<GeoPolygon> holes;
>>> >> >> >
>>> >> >> >    /** A list of edges */
>>> >> >> >    protected SidedPlane[] edges = null;
>>> >> >> > @@ -44,6 +46,8 @@ public class GeoConvexPolygon extends
>>> >> >> > GeoBasePolygon {
>>> >> >> >    protected double fullDistance = 0.0;
>>> >> >> >    /** Set to true when the polygon is complete */
>>> >> >> >    protected boolean isDone = false;
>>> >> >> > +
>>> >> >> > +  protected Membership eitherBound = new EitherBound();
>>> >> >> >
>>> >> >> >    /**
>>> >> >> >     * Create a convex polygon from a list of points.  The first
>>> >> >> > point
>>> >> >> > must be on the
>>> >> >> > @@ -52,8 +56,20 @@ public class GeoConvexPolygon extends
>>> >> >> > GeoBasePolygon
>>> >> >> > {
>>> >> >> >     *@param pointList is the list of points to create the polygon
>>> >> >> > from.
>>> >> >> >     */
>>> >> >> >    public GeoConvexPolygon(final PlanetModel planetModel, final
>>> >> >> > List<GeoPoint> pointList) {
>>> >> >> > +    this(planetModel, pointList, null);
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  /**
>>> >> >> > +   * Create a convex polygon from a list of points.  The first
>>> >> >> > point
>>> >> >> > must be on the
>>> >> >> > +   * external edge.
>>> >> >> > +   *@param planetModel is the planet model.
>>> >> >> > +   *@param pointList is the list of points to create the polygon
>>> >> >> > from.
>>> >> >> > +   *@param holes is the list of GeoPolygon objects that describe
>>> >> >> > holes
>>> >> >> > in the complex polygon.  Null == no holes.
>>> >> >> > +   */
>>> >> >> > +  public GeoConvexPolygon(final PlanetModel planetModel, final
>>> >> >> > List<GeoPoint> pointList, final List<GeoPolygon> holes) {
>>> >> >> >      super(planetModel);
>>> >> >> >      this.points = pointList;
>>> >> >> > +    this.holes = holes;
>>> >> >> >      this.isInternalEdges = new BitSet();
>>> >> >> >      done(false);
>>> >> >> >    }
>>> >> >> > @@ -66,10 +82,30 @@ public class GeoConvexPolygon extends
>>> >> >> > GeoBasePolygon
>>> >> >> > {
>>> >> >> >     *@param internalEdgeFlags is a bitset describing whether each
>>> >> >> > edge
>>> >> >> > is internal or not.
>>> >> >> >     *@param returnEdgeInternal is true when the final return edge
>>> >> >> > is
>>> >> >> > an
>>> >> >> > internal one.
>>> >> >> >     */
>>> >> >> > -  public GeoConvexPolygon(final PlanetModel planetModel, final
>>> >> >> > List<GeoPoint> pointList, final BitSet internalEdgeFlags,
>>> >> >> > -                          final boolean returnEdgeInternal) {
>>> >> >> > +  public GeoConvexPolygon(final PlanetModel planetModel,
>>> >> >> > +    final List<GeoPoint> pointList,
>>> >> >> > +    final BitSet internalEdgeFlags,
>>> >> >> > +    final boolean returnEdgeInternal) {
>>> >> >> > +    this(planetModel, pointList, null, internalEdgeFlags,
>>> >> >> > returnEdgeInternal);
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  /**
>>> >> >> > +   * Create a convex polygon from a list of points, keeping track
>>> >> >> > of
>>> >> >> > which boundaries
>>> >> >> > +   * are internal.  This is used when creating a polygon as a
>>> >> >> > building
>>> >> >> > block for another shape.
>>> >> >> > +   *@param planetModel is the planet model.
>>> >> >> > +   *@param pointList is the set of points to create the polygon
>>> >> >> > from.
>>> >> >> > +   *@param holes is the list of GeoPolygon objects that describe
>>> >> >> > holes
>>> >> >> > in the complex polygon.  Null == no holes.
>>> >> >> > +   *@param internalEdgeFlags is a bitset describing whether each
>>> >> >> > edge
>>> >> >> > is internal or not.
>>> >> >> > +   *@param returnEdgeInternal is true when the final return edge
>>> >> >> > is
>>> >> >> > an
>>> >> >> > internal one.
>>> >> >> > +   */
>>> >> >> > +  public GeoConvexPolygon(final PlanetModel planetModel,
>>> >> >> > +    final List<GeoPoint> pointList,
>>> >> >> > +    final List<GeoPolygon> holes,
>>> >> >> > +    final BitSet internalEdgeFlags,
>>> >> >> > +    final boolean returnEdgeInternal) {
>>> >> >> >      super(planetModel);
>>> >> >> >      this.points = pointList;
>>> >> >> > +    this.holes = holes;
>>> >> >> >      this.isInternalEdges = internalEdgeFlags;
>>> >> >> >      done(returnEdgeInternal);
>>> >> >> >    }
>>> >> >> > @@ -81,9 +117,27 @@ public class GeoConvexPolygon extends
>>> >> >> > GeoBasePolygon
>>> >> >> > {
>>> >> >> >     *@param startLatitude is the latitude of the first point.
>>> >> >> >     *@param startLongitude is the longitude of the first point.
>>> >> >> >     */
>>> >> >> > -  public GeoConvexPolygon(final PlanetModel planetModel, final
>>> >> >> > double
>>> >> >> > startLatitude, final double startLongitude) {
>>> >> >> > +  public GeoConvexPolygon(final PlanetModel planetModel,
>>> >> >> > +    final double startLatitude,
>>> >> >> > +    final double startLongitude) {
>>> >> >> > +    this(planetModel, startLatitude, startLongitude, null);
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  /**
>>> >> >> > +   * Create a convex polygon, with a starting latitude and
>>> >> >> > longitude.
>>> >> >> > +   * Accepts only values in the following ranges: lat: {@code
>>> >> >> > -PI/2
>>> >> >> > ->
>>> >> >> > PI/2}, lon: {@code -PI -> PI}
>>> >> >> > +   *@param planetModel is the planet model.
>>> >> >> > +   *@param startLatitude is the latitude of the first point.
>>> >> >> > +   *@param startLongitude is the longitude of the first point.
>>> >> >> > +   *@param holes is the list of GeoPolygon objects that describe
>>> >> >> > holes
>>> >> >> > in the complex polygon.  Null == no holes.
>>> >> >> > +   */
>>> >> >> > +  public GeoConvexPolygon(final PlanetModel planetModel,
>>> >> >> > +    final double startLatitude,
>>> >> >> > +    final double startLongitude,
>>> >> >> > +    final List<GeoPolygon> holes) {
>>> >> >> >      super(planetModel);
>>> >> >> >      points = new ArrayList<>();
>>> >> >> > +    this.holes = holes;
>>> >> >> >      isInternalEdges = new BitSet();
>>> >> >> >      points.add(new GeoPoint(planetModel, startLatitude,
>>> >> >> > startLongitude));
>>> >> >> >    }
>>> >> >> > @@ -138,12 +192,6 @@ public class GeoConvexPolygon extends
>>> >> >> > GeoBasePolygon {
>>> >> >> >        edges[i] = sp;
>>> >> >> >        notableEdgePoints[i] = new GeoPoint[]{start, end};
>>> >> >> >      }
>>> >> >> > -    createCenterPoint();
>>> >> >> > -  }
>>> >> >> > -
>>> >> >> > -  /** Compute a reasonable center point.
>>> >> >> > -   */
>>> >> >> > -  protected void createCenterPoint() {
>>> >> >> >      // In order to naively confirm that the polygon is convex, I
>>> >> >> > would
>>> >> >> > need to
>>> >> >> >      // check every edge, and verify that every point (other than
>>> >> >> > the
>>> >> >> > edge endpoints)
>>> >> >> >      // is within the edge's sided plane.  This is an order n^2
>>> >> >> > operation.  That's still
>>> >> >> > @@ -157,6 +205,7 @@ public class GeoConvexPolygon extends
>>> >> >> > GeoBasePolygon
>>> >> >> > {
>>> >> >> >          }
>>> >> >> >        }
>>> >> >> >      }
>>> >> >> > +    // Pick an edge point arbitrarily
>>> >> >> >      edgePoints = new GeoPoint[]{points.get(0)};
>>> >> >> >    }
>>> >> >> >
>>> >> >> > @@ -176,6 +225,13 @@ public class GeoConvexPolygon extends
>>> >> >> > GeoBasePolygon {
>>> >> >> >        if (!edge.isWithin(x, y, z))
>>> >> >> >          return false;
>>> >> >> >      }
>>> >> >> > +    if (holes != null) {
>>> >> >> > +      for (final GeoPolygon polygon : holes) {
>>> >> >> > +        if (polygon.isWithin(x, y, z)) {
>>> >> >> > +          return false;
>>> >> >> > +        }
>>> >> >> > +      }
>>> >> >> > +    }
>>> >> >> >      return true;
>>> >> >> >    }
>>> >> >> >
>>> >> >> > @@ -191,26 +247,54 @@ public class GeoConvexPolygon extends
>>> >> >> > GeoBasePolygon {
>>> >> >> >        final SidedPlane edge = edges[edgeIndex];
>>> >> >> >        final GeoPoint[] points =
>>> >> >> > this.notableEdgePoints[edgeIndex];
>>> >> >> >        if (!isInternalEdges.get(edgeIndex)) {
>>> >> >> > -        //System.err.println(" non-internal edge "+edge);
>>> >> >> > -        // Edges flagged as 'internal only' are excluded from the
>>> >> >> > matching
>>> >> >> > -        // Construct boundaries
>>> >> >> > -        final Membership[] membershipBounds = new
>>> >> >> > Membership[edges.length - 1];
>>> >> >> > -        int count = 0;
>>> >> >> > -        for (int otherIndex = 0; otherIndex < edges.length;
>>> >> >> > otherIndex++) {
>>> >> >> > -          if (otherIndex != edgeIndex) {
>>> >> >> > -            membershipBounds[count++] = edges[otherIndex];
>>> >> >> > -          }
>>> >> >> > -        }
>>> >> >> > -        if (edge.intersects(planetModel, p, notablePoints,
>>> >> >> > points,
>>> >> >> > bounds, membershipBounds)) {
>>> >> >> > +        if (edge.intersects(planetModel, p, notablePoints,
>>> >> >> > points,
>>> >> >> > bounds, eitherBound)) {
>>> >> >> >            //System.err.println(" intersects!");
>>> >> >> >            return true;
>>> >> >> >          }
>>> >> >> >        }
>>> >> >> >      }
>>> >> >> > +    if (holes != null) {
>>> >> >> > +      // Each hole needs to be looked at for intersection too,
>>> >> >> > since
>>> >> >> > a
>>> >> >> > shape can be entirely within the hole
>>> >> >> > +      for (final GeoPolygon hole : holes) {
>>> >> >> > +        if (hole.intersects(p, notablePoints, bounds)) {
>>> >> >> > +          return true;
>>> >> >> > +        }
>>> >> >> > +      }
>>> >> >> > +    }
>>> >> >> >      //System.err.println(" no intersection");
>>> >> >> >      return false;
>>> >> >> >    }
>>> >> >> >
>>> >> >> > +  /** A membership implementation representing polygon edges that
>>> >> >> > all
>>> >> >> > must apply.
>>> >> >> > +   */
>>> >> >> > +  protected class EitherBound implements Membership {
>>> >> >> > +    /** Constructor.
>>> >> >> > +      */
>>> >> >> > +    public EitherBound() {
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    @Override
>>> >> >> > +    public boolean isWithin(final Vector v) {
>>> >> >> > +      for (final SidedPlane edge : edges) {
>>> >> >> > +        if (!edge.isWithin(v)) {
>>> >> >> > +          return false;
>>> >> >> > +        }
>>> >> >> > +      }
>>> >> >> > +      return true;
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    @Override
>>> >> >> > +    public boolean isWithin(final double x, final double y, final
>>> >> >> > double z) {
>>> >> >> > +      for (final SidedPlane edge : edges) {
>>> >> >> > +        if (!edge.isWithin(x, y, z)) {
>>> >> >> > +          return false;
>>> >> >> > +        }
>>> >> >> > +      }
>>> >> >> > +      return true;
>>> >> >> > +    }
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +
>>> >> >> >    @Override
>>> >> >> >    public void getBounds(Bounds bounds) {
>>> >> >> >      super.getBounds(bounds);
>>> >> >> > @@ -221,17 +305,8 @@ public class GeoConvexPolygon extends
>>> >> >> > GeoBasePolygon {
>>> >> >> >      }
>>> >> >> >
>>> >> >> >      // Add planes with membership.
>>> >> >> > -    for (int edgeIndex = 0; edgeIndex < edges.length;
>>> >> >> > edgeIndex++) {
>>> >> >> > -      final SidedPlane edge = edges[edgeIndex];
>>> >> >> > -      // Construct boundaries
>>> >> >> > -      final Membership[] membershipBounds = new
>>> >> >> > Membership[edges.length
>>> >> >> > - 1];
>>> >> >> > -      int count = 0;
>>> >> >> > -      for (int otherIndex = 0; otherIndex < edges.length;
>>> >> >> > otherIndex++)
>>> >> >> > {
>>> >> >> > -        if (otherIndex != edgeIndex) {
>>> >> >> > -          membershipBounds[count++] = edges[otherIndex];
>>> >> >> > -        }
>>> >> >> > -      }
>>> >> >> > -      bounds.addPlane(planetModel, edge, membershipBounds);
>>> >> >> > +    for (final SidedPlane edge : edges) {
>>> >> >> > +      bounds.addPlane(planetModel, edge, eitherBound);
>>> >> >> >      }
>>> >> >> >    }
>>> >> >> >
>>> >> >> > @@ -244,16 +319,8 @@ public class GeoConvexPolygon extends
>>> >> >> > GeoBasePolygon {
>>> >> >> >          minimumDistance = newDist;
>>> >> >> >        }
>>> >> >> >      }
>>> >> >> > -    for (int edgeIndex = 0; edgeIndex < edges.length;
>>> >> >> > edgeIndex++) {
>>> >> >> > -      final Plane edgePlane = edges[edgeIndex];
>>> >> >> > -      final Membership[] membershipBounds = new
>>> >> >> > Membership[edges.length
>>> >> >> > - 1];
>>> >> >> > -      int count = 0;
>>> >> >> > -      for (int otherIndex = 0; otherIndex < edges.length;
>>> >> >> > otherIndex++)
>>> >> >> > {
>>> >> >> > -        if (otherIndex != edgeIndex) {
>>> >> >> > -          membershipBounds[count++] = edges[otherIndex];
>>> >> >> > -        }
>>> >> >> > -      }
>>> >> >> > -      final double newDist =
>>> >> >> > distanceStyle.computeDistance(planetModel,
>>> >> >> > edgePlane, x, y, z, membershipBounds);
>>> >> >> > +    for (final Plane edgePlane : edges) {
>>> >> >> > +      final double newDist =
>>> >> >> > distanceStyle.computeDistance(planetModel,
>>> >> >> > edgePlane, x, y, z, eitherBound);
>>> >> >> >        if (newDist < minimumDistance) {
>>> >> >> >          minimumDistance = newDist;
>>> >> >> >        }
>>> >> >> > @@ -270,6 +337,14 @@ public class GeoConvexPolygon extends
>>> >> >> > GeoBasePolygon {
>>> >> >> >        return false;
>>> >> >> >      if (!other.isInternalEdges.equals(isInternalEdges))
>>> >> >> >        return false;
>>> >> >> > +    if (other.holes != null || holes != null) {
>>> >> >> > +      if (other.holes == null || holes == null) {
>>> >> >> > +        return false;
>>> >> >> > +      }
>>> >> >> > +      if (!other.holes.equals(holes)) {
>>> >> >> > +        return false;
>>> >> >> > +      }
>>> >> >> > +    }
>>> >> >> >      return (other.points.equals(points));
>>> >> >> >    }
>>> >> >> >
>>> >> >> > @@ -277,12 +352,15 @@ public class GeoConvexPolygon extends
>>> >> >> > GeoBasePolygon {
>>> >> >> >    public int hashCode() {
>>> >> >> >      int result = super.hashCode();
>>> >> >> >      result = 31 * result + points.hashCode();
>>> >> >> > +    if (holes != null) {
>>> >> >> > +      result = 31 * result + holes.hashCode();
>>> >> >> > +    }
>>> >> >> >      return result;
>>> >> >> >    }
>>> >> >> >
>>> >> >> >    @Override
>>> >> >> >    public String toString() {
>>> >> >> > -    return "GeoConvexPolygon: {planetmodel=" + planetModel + ",
>>> >> >> > points=" + points + "}";
>>> >> >> > +    return "GeoConvexPolygon: {planetmodel=" + planetModel + ",
>>> >> >> > points=" + points + ((holes== null)?"":", holes=" + holes) + "}";
>>> >> >> >    }
>>> >> >> >  }
>>> >> >> >
>>> >> >> >
>>> >> >> >
>>> >> >> >
>>> >> >> >
>>> >> >> > http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/98e04cf4/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 8ee4290..f076d41 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
>>> >> >> > @@ -19,6 +19,12 @@ package org.apache.lucene.spatial3d.geom;
>>> >> >> >  import java.util.ArrayList;
>>> >> >> >  import java.util.BitSet;
>>> >> >> >  import java.util.List;
>>> >> >> > +import java.util.Random;
>>> >> >> > +import java.util.Iterator;
>>> >> >> > +import java.util.Set;
>>> >> >> > +import java.util.HashSet;
>>> >> >> > +import java.util.Map;
>>> >> >> > +import java.util.HashMap;
>>> >> >> >
>>> >> >> >  /**
>>> >> >> >   * Class which constructs a GeoMembershipShape representing an
>>> >> >> > arbitrary polygon.
>>> >> >> > @@ -37,138 +43,619 @@ public class GeoPolygonFactory {
>>> >> >> >     *                         its neighbors determines
>>> >> >> > inside/outside
>>> >> >> > for the entire polygon.
>>> >> >> >     * @return a GeoPolygon corresponding to what was specified.
>>> >> >> >     */
>>> >> >> > -  public static GeoPolygon makeGeoPolygon(final PlanetModel
>>> >> >> > planetModel, final List<GeoPoint> pointList, final int
>>> >> >> > convexPointIndex) {
>>> >> >> > +  public static GeoPolygon makeGeoPolygon(final PlanetModel
>>> >> >> > planetModel,
>>> >> >> > +    final List<GeoPoint> pointList,
>>> >> >> > +    final int convexPointIndex) {
>>> >> >> > +    return makeGeoPolygon(planetModel, pointList,
>>> >> >> > convexPointIndex,
>>> >> >> > null);
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  /**
>>> >> >> > +   * Create a GeoMembershipShape of the right kind given the
>>> >> >> > specified
>>> >> >> > bounds.
>>> >> >> > +   *
>>> >> >> > +   * @param pointList        is a list of the GeoPoints to build
>>> >> >> > an
>>> >> >> > arbitrary polygon out of.
>>> >> >> > +   * @param convexPointIndex is the index of a single convex
>>> >> >> > point
>>> >> >> > whose conformation with
>>> >> >> > +   *                         its neighbors determines
>>> >> >> > inside/outside
>>> >> >> > for the entire polygon.
>>> >> >> > +   * @param holes is a list of polygons representing "holes" in
>>> >> >> > the
>>> >> >> > outside polygon.  Null == none.
>>> >> >> > +   * @return a GeoPolygon corresponding to what was specified.
>>> >> >> > +   */
>>> >> >> > +  public static GeoPolygon makeGeoPolygon(final PlanetModel
>>> >> >> > planetModel,
>>> >> >> > +    final List<GeoPoint> pointList,
>>> >> >> > +    final int convexPointIndex,
>>> >> >> > +    final List<GeoPolygon> holes) {
>>> >> >> >      // The basic operation uses a set of points, two points
>>> >> >> > determining
>>> >> >> > one particular edge, and a sided plane
>>> >> >> >      // describing membership.
>>> >> >> >      return buildPolygonShape(planetModel, pointList,
>>> >> >> > convexPointIndex,
>>> >> >> > getLegalIndex(convexPointIndex + 1, pointList.size()),
>>> >> >> >          new
>>> >> >> > SidedPlane(pointList.get(getLegalIndex(convexPointIndex
>>> >> >> > -
>>> >> >> > 1, pointList.size())),
>>> >> >> >              pointList.get(convexPointIndex),
>>> >> >> > pointList.get(getLegalIndex(convexPointIndex + 1,
>>> >> >> > pointList.size()))),
>>> >> >> > -        false);
>>> >> >> > +        false,
>>> >> >> > +        holes,
>>> >> >> > +        null);
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  /** Create a GeoPolygon using the specified points and holes,
>>> >> >> > using
>>> >> >> > order to determine
>>> >> >> > +   * siding of the polygon.  Much like ESRI, this method uses
>>> >> >> > clockwise
>>> >> >> > to indicate the space
>>> >> >> > +   * on the same side of the shape as being inside, and
>>> >> >> > counter-clockwise to indicate the
>>> >> >> > +   * space on the opposite side as being inside.
>>> >> >> > +   * @param pointList is a list of the GeoPoints to build an
>>> >> >> > arbitrary
>>> >> >> > polygon out of.  If points go
>>> >> >> > +   *  clockwise from a given pole, then that pole should be
>>> >> >> > within
>>> >> >> > the
>>> >> >> > polygon.  If points go
>>> >> >> > +   *  counter-clockwise, then that pole should be outside the
>>> >> >> > polygon.
>>> >> >> > +   * @return a GeoPolygon corresponding to what was specified.
>>> >> >> > +   */
>>> >> >> > +  public static GeoPolygon makeGeoPolygon(final PlanetModel
>>> >> >> > planetModel,
>>> >> >> > +    final List<GeoPoint> pointList) {
>>> >> >> > +    return makeGeoPolygon(planetModel, pointList, null);
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  /** Create a GeoPolygon using the specified points and holes,
>>> >> >> > using
>>> >> >> > order to determine
>>> >> >> > +   * siding of the polygon.  Much like ESRI, this method uses
>>> >> >> > clockwise
>>> >> >> > to indicate the space
>>> >> >> > +   * on the same side of the shape as being inside, and
>>> >> >> > counter-clockwise to indicate the
>>> >> >> > +   * space on the opposite side as being inside.
>>> >> >> > +   * @param pointList is a list of the GeoPoints to build an
>>> >> >> > arbitrary
>>> >> >> > polygon out of.  If points go
>>> >> >> > +   *  clockwise from a given pole, then that pole should be
>>> >> >> > within
>>> >> >> > the
>>> >> >> > polygon.  If points go
>>> >> >> > +   *  counter-clockwise, then that pole should be outside the
>>> >> >> > polygon.
>>> >> >> > +   * @param holes is a list of polygons representing "holes" in
>>> >> >> > the
>>> >> >> > outside polygon.  Null == none.
>>> >> >> > +   * @return a GeoPolygon corresponding to what was specified.
>>> >> >> > +   */
>>> >> >> > +  public static GeoPolygon makeGeoPolygon(final PlanetModel
>>> >> >> > planetModel,
>>> >> >> > +    final List<GeoPoint> pointList,
>>> >> >> > +    final List<GeoPolygon> holes) {
>>> >> >> > +    // 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);
>>> >> >> > +    while (true) {
>>> >> >> > +      // Pick the next random pole
>>> >> >> > +      final double poleLat = generator.nextDouble() * Math.PI -
>>> >> >> > Math.PI
>>> >> >> > * 0.5;
>>> >> >> > +      final double poleLon = generator.nextDouble() * Math.PI *
>>> >> >> > 2.0
>>> >> >> > -
>>> >> >> > Math.PI;
>>> >> >> > +      final GeoPoint pole = new GeoPoint(planetModel, poleLat,
>>> >> >> > poleLon);
>>> >> >> > +      // Is it inside or outside?
>>> >> >> > +      final Boolean isPoleInside = isInsidePolygon(pole,
>>> >> >> > pointList);
>>> >> >> > +      if (isPoleInside != null) {
>>> >> >> > +        // Legal pole
>>> >> >> > +        return makeGeoPolygon(planetModel, pointList, holes,
>>> >> >> > pole,
>>> >> >> > isPoleInside);
>>> >> >> > +      }
>>> >> >> > +      // If pole choice was illegal, try another one
>>> >> >> > +    }
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  /**
>>> >> >> > +   * Create a GeoPolygon using the specified points and holes and
>>> >> >> > a
>>> >> >> > test point.
>>> >> >> > +   *
>>> >> >> > +   * @param pointList        is a list of the GeoPoints to build
>>> >> >> > an
>>> >> >> > arbitrary polygon out of.
>>> >> >> > +   * @param holes is a list of polygons representing "holes" in
>>> >> >> > the
>>> >> >> > outside polygon.  Null == none.
>>> >> >> > +   * @param testPoint is a test point that is either known to be
>>> >> >> > within
>>> >> >> > the polygon area, or not.
>>> >> >> > +   * @param testPointInside is true if the test point is within
>>> >> >> > the
>>> >> >> > area, false otherwise.
>>> >> >> > +   * @return a GeoPolygon corresponding to what was specified.
>>> >> >> > +   */
>>> >> >> > +  public static GeoPolygon makeGeoPolygon(final PlanetModel
>>> >> >> > planetModel,
>>> >> >> > +    final List<GeoPoint> pointList,
>>> >> >> > +    final List<GeoPolygon> holes,
>>> >> >> > +    final GeoPoint testPoint,
>>> >> >> > +    final boolean testPointInside) {
>>> >> >> > +    // We will be trying twice to find the right GeoPolygon,
>>> >> >> > using
>>> >> >> > alternate siding choices for the first polygon
>>> >> >> > +    // side.  While this looks like it might be 2x as expensive
>>> >> >> > as
>>> >> >> > it
>>> >> >> > could be, there's really no other choice I can
>>> >> >> > +    // find.
>>> >> >> > +    final SidedPlane initialPlane = new SidedPlane(testPoint,
>>> >> >> > pointList.get(0), pointList.get(1));
>>> >> >> > +    // We don't know if this is the correct siding choice.  We
>>> >> >> > will
>>> >> >> > only know as we build the complex polygon.
>>> >> >> > +    // So we need to be prepared to try both possibilities.
>>> >> >> > +    final GeoPolygon trial = buildPolygonShape(planetModel,
>>> >> >> > pointList,
>>> >> >> > 0, 1, initialPlane, false, holes, testPoint);
>>> >> >> > +    if (trial == null) {
>>> >> >> > +      // The testPoint was within the shape.  Was that intended?
>>> >> >> > +      if (testPointInside) {
>>> >> >> > +        // Yes: build it for real
>>> >> >> > +        return buildPolygonShape(planetModel, pointList, 0, 1,
>>> >> >> > initialPlane, false, holes, null);
>>> >> >> > +      }
>>> >> >> > +      // No: do the complement and return that.
>>> >> >> > +      return buildPolygonShape(planetModel, pointList, 0, 1, new
>>> >> >> > SidedPlane(initialPlane), false, holes, null);
>>> >> >> > +    } else {
>>> >> >> > +      // The testPoint was outside the shape.  Was that intended?
>>> >> >> > +      if (!testPointInside) {
>>> >> >> > +        // Yes: return what we just built
>>> >> >> > +        return trial;
>>> >> >> > +      }
>>> >> >> > +      // No: return the complement
>>> >> >> > +      return buildPolygonShape(planetModel, pointList, 0, 1, new
>>> >> >> > SidedPlane(initialPlane), false, holes, null);
>>> >> >> > +    }
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  /** For a specified point and a list of poly points, determine
>>> >> >> > based
>>> >> >> > on point order whether the
>>> >> >> > +   * point should be considered in or out of the polygon.
>>> >> >> > +   * @param point is the point to check.
>>> >> >> > +   * @param polyPoints is the list of points comprising the
>>> >> >> > polygon.
>>> >> >> > +   * @return null if the point is illegal, otherwise false if the
>>> >> >> > point
>>> >> >> > is inside and true if the point is outside
>>> >> >> > +   * of the polygon.
>>> >> >> > +   */
>>> >> >> > +  protected static Boolean isInsidePolygon(final GeoPoint point,
>>> >> >> > final
>>> >> >> > List<GeoPoint> polyPoints) {
>>> >> >> > +    // First, compute sine and cosine of pole point latitude and
>>> >> >> > longitude
>>> >> >> > +    final double norm = 1.0 / point.magnitude();
>>> >> >> > +    final double xyDenom = Math.sqrt(point.x * point.x + point.y
>>> >> >> > *
>>> >> >> > point.y);
>>> >> >> > +    final double sinLatitude = point.z * norm;
>>> >> >> > +    final double cosLatitude = xyDenom * norm;
>>> >> >> > +    final double sinLongitude;
>>> >> >> > +    final double cosLongitude;
>>> >> >> > +    if (Math.abs(xyDenom) < Vector.MINIMUM_RESOLUTION) {
>>> >> >> > +      sinLongitude = 0.0;
>>> >> >> > +      cosLongitude = 1.0;
>>> >> >> > +    } else {
>>> >> >> > +      final double xyNorm = 1.0 / xyDenom;
>>> >> >> > +      sinLongitude = point.y * xyNorm;
>>> >> >> > +      cosLongitude = point.x * xyNorm;
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    // Now, compute the incremental arc distance around the
>>> >> >> > points
>>> >> >> > of
>>> >> >> > the polygon
>>> >> >> > +    double arcDistance = 0.0;
>>> >> >> > +    Double prevAngle = null;
>>> >> >> > +    for (final GeoPoint polyPoint : polyPoints) {
>>> >> >> > +      final Double angle = computeAngle(polyPoint, sinLatitude,
>>> >> >> > cosLatitude, sinLongitude, cosLongitude);
>>> >> >> > +      if (angle == null) {
>>> >> >> > +        return null;
>>> >> >> > +      }
>>> >> >> > +      System.out.println("Computed angle: "+angle);
>>> >> >> > +      if (prevAngle != null) {
>>> >> >> > +        // Figure out delta between prevAngle and current angle,
>>> >> >> > and
>>> >> >> > add it to arcDistance
>>> >> >> > +        double angleDelta = angle - prevAngle;
>>> >> >> > +        if (angleDelta < -Math.PI) {
>>> >> >> > +          angleDelta += Math.PI * 2.0;
>>> >> >> > +        }
>>> >> >> > +        if (angleDelta > Math.PI) {
>>> >> >> > +          angleDelta -= Math.PI * 2.0;
>>> >> >> > +        }
>>> >> >> > +        if (Math.abs(angleDelta - Math.PI) <
>>> >> >> > Vector.MINIMUM_RESOLUTION)
>>> >> >> > {
>>> >> >> > +          return null;
>>> >> >> > +        }
>>> >> >> > +        System.out.println(" angle delta = "+angleDelta);
>>> >> >> > +        arcDistance += angleDelta;
>>> >> >> > +      }
>>> >> >> > +      prevAngle = angle;
>>> >> >> > +    }
>>> >> >> > +    if (prevAngle != null) {
>>> >> >> > +      final Double lastAngle = computeAngle(polyPoints.get(0),
>>> >> >> > sinLatitude, cosLatitude, sinLongitude, cosLongitude);
>>> >> >> > +      if (lastAngle == null) {
>>> >> >> > +        return null;
>>> >> >> > +      }
>>> >> >> > +      System.out.println("Computed last angle: "+lastAngle);
>>> >> >> > +      // Figure out delta and add it
>>> >> >> > +      double angleDelta = lastAngle - prevAngle;
>>> >> >> > +      if (angleDelta < -Math.PI) {
>>> >> >> > +        angleDelta += Math.PI * 2.0;
>>> >> >> > +      }
>>> >> >> > +      if (angleDelta > Math.PI) {
>>> >> >> > +        angleDelta -= Math.PI * 2.0;
>>> >> >> > +      }
>>> >> >> > +      if (Math.abs(angleDelta - Math.PI) <
>>> >> >> > Vector.MINIMUM_RESOLUTION) {
>>> >> >> > +        return null;
>>> >> >> > +      }
>>> >> >> > +      System.out.println(" angle delta = "+angleDelta);
>>> >> >> > +      arcDistance += angleDelta;
>>> >> >> > +    }
>>> >> >> > +    // Clockwise == inside == negative
>>> >> >> > +    System.out.println("Arcdistance = "+arcDistance);
>>> >> >> > +    if (Math.abs(arcDistance) < Vector.MINIMUM_RESOLUTION) {
>>> >> >> > +      // No idea what direction, so try another pole.
>>> >> >> > +      return null;
>>> >> >> > +    }
>>> >> >> > +    return arcDistance < 0.0;
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  protected static Double computeAngle(final GeoPoint point,
>>> >> >> > +    final double sinLatitude,
>>> >> >> > +    final double cosLatitude,
>>> >> >> > +    final double sinLongitude,
>>> >> >> > +    final double cosLongitude) {
>>> >> >> > +    // Coordinate rotation formula:
>>> >> >> > +    // x1 = x0 cos T - y0 sin T
>>> >> >> > +    // y1 = x0 sin T + y0 cos T
>>> >> >> > +    // We need to rotate the point in question into the
>>> >> >> > coordinate
>>> >> >> > frame specified by
>>> >> >> > +    // the lat and lon trig functions.
>>> >> >> > +    // To do this we need to do two rotations on it.  First
>>> >> >> > rotation
>>> >> >> > is
>>> >> >> > in x/y.  Second rotation is in x/z.
>>> >> >> > +    // So:
>>> >> >> > +    // x1 = x0 cos az - y0 sin az
>>> >> >> > +    // y1 = x0 sin az + y0 cos az
>>> >> >> > +    // z1 = z0
>>> >> >> > +    // x2 = x1 cos al - z1 sin al
>>> >> >> > +    // y2 = y1
>>> >> >> > +    // z2 = x1 sin al + z1 cos al
>>> >> >> > +
>>> >> >> > +    final double x1 = point.x * cosLongitude - point.y *
>>> >> >> > sinLongitude;
>>> >> >> > +    final double y1 = point.x * sinLongitude + point.y *
>>> >> >> > cosLongitude;
>>> >> >> > +    final double z1 = point.z;
>>> >> >> > +    //final double x2 = x1 * cosLatitude - z1 * sinLatitude;
>>> >> >> > +    final double y2 = y1;
>>> >> >> > +    final double z2 = x1 * sinLatitude + z1 * cosLatitude;
>>> >> >> > +
>>> >> >> > +    // Now we should be looking down the X axis; the original
>>> >> >> > point
>>> >> >> > has
>>> >> >> > rotated coordinates (N, 0, 0).
>>> >> >> > +    // So we can just compute the angle using y2 and z2.  (If
>>> >> >> > Math.sqrt(y2*y2 + z2 * z2) is 0.0, then the point is on the pole
>>> >> >> > and
>>> >> >> > we need
>>> >> >> > another one).
>>> >> >> > +    if (Math.sqrt(y2*y2 + z2*z2) < Vector.MINIMUM_RESOLUTION) {
>>> >> >> > +      return null;
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    return Math.atan2(z2, y2);
>>> >> >> >    }
>>> >> >> >
>>> >> >> > -  /** Build a GeoMembershipShape given points, starting edge, and
>>> >> >> > whether starting edge is internal or not.
>>> >> >> > +  /** Build a GeoPolygon out of one concave part and multiple
>>> >> >> > convex
>>> >> >> > parts given points, starting edge, and whether starting edge is
>>> >> >> > internal or
>>> >> >> > not.
>>> >> >> >     * @param pointsList        is a list of the GeoPoints to build
>>> >> >> > an
>>> >> >> > arbitrary polygon out of.
>>> >> >> > -   * @param startPointIndex is one of the points constituting the
>>> >> >> > starting edge.
>>> >> >> > -   * @param endPointIndex is another of the points constituting
>>> >> >> > the
>>> >> >> > starting edge.
>>> >> >> > +   * @param startPointIndex is the first of the points,
>>> >> >> > constituting
>>> >> >> > the starting edge.
>>> >> >> >     * @param startingEdge is the plane describing the starting
>>> >> >> > edge.
>>> >> >> >     * @param isInternalEdge is true if the specified edge is an
>>> >> >> > internal
>>> >> >> > one.
>>> >> >> > -   * @return a GeoMembershipShape corresponding to what was
>>> >> >> > specified.
>>> >> >> > +   * @param holes is the list of holes in the polygon, or null if
>>> >> >> > none.
>>> >> >> > +   * @param testPoint is an (optional) test point, which will be
>>> >> >> > used
>>> >> >> > to determine if we are generating
>>> >> >> > +   *  a shape with the proper sidedness.  It is passed in only
>>> >> >> > when
>>> >> >> > the
>>> >> >> > test point is supposed to be outside
>>> >> >> > +   *  of the generated polygon.  In this case, if the generated
>>> >> >> > polygon
>>> >> >> > is found to contain the point, the
>>> >> >> > +   *  method exits early with a null return value.
>>> >> >> > +   *  This only makes sense in the context of evaluating both
>>> >> >> > possible
>>> >> >> > choices and using logic to determine
>>> >> >> > +   *  which result to use.  If the test point is supposed to be
>>> >> >> > within
>>> >> >> > the shape, then it must be outside of the
>>> >> >> > +   *  complement shape.  If the test point is supposed to be
>>> >> >> > outside
>>> >> >> > the shape, then it must be outside of the
>>> >> >> > +   *  original shape.  Either way, we can figure out the right
>>> >> >> > thing
>>> >> >> > to
>>> >> >> > use.
>>> >> >> > +   * @return a GeoMembershipShape corresponding to what was
>>> >> >> > specified,
>>> >> >> > or null if what was specified
>>> >> >> > +   *  was inconsistent with what we generated.  Specifically, if
>>> >> >> > we
>>> >> >> > specify an exterior point that is
>>> >> >> > +   *  found in the interior of the shape we create here we return
>>> >> >> > null,
>>> >> >> > which is a signal that we chose
>>> >> >> > +   *  our initial plane sidedness backwards.
>>> >> >> >     */
>>> >> >> > -  public static GeoPolygon buildPolygonShape(final PlanetModel
>>> >> >> > planetModel, final List<GeoPoint> pointsList, final int
>>> >> >> > startPointIndex,
>>> >> >> > final int endPointIndex, final SidedPlane startingEdge, final
>>> >> >> > boolean
>>> >> >> > isInternalEdge) {
>>> >> >> > -    // Algorithm as follows:
>>> >> >> > -    // Start with sided edge.  Go through all points in some
>>> >> >> > order.
>>> >> >> > For each new point, determine if the point is within all edges
>>> >> >> > considered so
>>> >> >> > far.
>>> >> >> > -    // If not, put it into a list of points for recursion.  If it
>>> >> >> > is
>>> >> >> > within, add new edge and keep going.
>>> >> >> > -    // Once we detect a point that is within, if there are points
>>> >> >> > put
>>> >> >> > aside for recursion, then call recursively.
>>> >> >> > +  public static GeoPolygon buildPolygonShape(
>>> >> >> > +    final PlanetModel planetModel,
>>> >> >> > +    final List<GeoPoint> pointsList,
>>> >> >> > +    final int startPointIndex,
>>> >> >> > +    final int endPointIndex,
>>> >> >> > +    final SidedPlane startingEdge,
>>> >> >> > +    final boolean isInternalEdge,
>>> >> >> > +    final List<GeoPolygon> holes,
>>> >> >> > +    final GeoPoint testPoint) {
>>> >> >> >
>>> >> >> > -    // Current composite.  This is what we'll actually be
>>> >> >> > returning.
>>> >> >> > +    // It could be the case that we need a concave polygon.  So
>>> >> >> > we
>>> >> >> > need
>>> >> >> > to try and look for that case
>>> >> >> > +    // as part of the general code for constructing complex
>>> >> >> > polygons.
>>> >> >> > +
>>> >> >> > +    // Note that there can be only one concave polygon.
>>> >> >> > +
>>> >> >> > +    // The code here must keep track of two lists of sided
>>> >> >> > planes.
>>> >> >> > The
>>> >> >> > first list contains the planes consistent with
>>> >> >> > +    // a concave polygon.  This list will grow and shrink.  The
>>> >> >> > second
>>> >> >> > list is built starting at the current edge that
>>> >> >> > +    // was last consistent with the concave polygon, and contains
>>> >> >> > all
>>> >> >> > edges consistent with a convex polygon.
>>> >> >> > +    // When that sequence of edges is done, then an internal edge
>>> >> >> > is
>>> >> >> > created and the identified points are converted to a
>>> >> >> > +    // convex polygon.  That internal edge is used to extend the
>>> >> >> > list
>>> >> >> > of edges in the concave polygon edge list.
>>> >> >> > +
>>> >> >> > +    // The edge buffer.
>>> >> >> > +    final EdgeBuffer edgeBuffer = new EdgeBuffer(pointsList,
>>> >> >> > startPointIndex, endPointIndex, startingEdge, isInternalEdge);
>>> >> >> > +
>>> >> >> > +    // Current composite.  This is what we'll actually be
>>> >> >> > returning.
>>> >> >> > This will have a number of convex polygons, and
>>> >> >> > +    // maybe a single concave one too.
>>> >> >> >      final GeoCompositePolygon rval = new GeoCompositePolygon();
>>> >> >> >
>>> >> >> > -    final List<GeoPoint> recursionList = new
>>> >> >> > ArrayList<GeoPoint>();
>>> >> >> > -    final List<GeoPoint> currentList = new ArrayList<GeoPoint>();
>>> >> >> > -    final BitSet internalEdgeList = new BitSet();
>>> >> >> > -    final List<SidedPlane> currentPlanes = new
>>> >> >> > ArrayList<SidedPlane>();
>>> >> >> > -
>>> >> >> > -    // Initialize the current list and current planes
>>> >> >> > -    currentList.add(pointsList.get(startPointIndex));
>>> >> >> > -    currentList.add(pointsList.get(endPointIndex));
>>> >> >> > -    internalEdgeList.set(currentPlanes.size(), isInternalEdge);
>>> >> >> > -    currentPlanes.add(startingEdge);
>>> >> >> > -
>>> >> >> > -    // Now, scan all remaining points, in order.  We'll use an
>>> >> >> > index
>>> >> >> > and just add to it.
>>> >> >> > -    for (int i = 0; i < pointsList.size() - 2; i++) {
>>> >> >> > -      GeoPoint newPoint = pointsList.get(getLegalIndex(i +
>>> >> >> > endPointIndex + 1, pointsList.size()));
>>> >> >> > -      if (isWithin(newPoint, currentPlanes)) {
>>> >> >> > -        // Construct a sided plane based on the last two points,
>>> >> >> > and
>>> >> >> > the previous point
>>> >> >> > -        SidedPlane newBoundary = new
>>> >> >> > SidedPlane(currentList.get(currentList.size() - 2), newPoint,
>>> >> >> > currentList.get(currentList.size() - 1));
>>> >> >> > -        // Construct a sided plane based on the return trip
>>> >> >> > -        SidedPlane returnBoundary = new
>>> >> >> > SidedPlane(currentList.get(currentList.size() - 1),
>>> >> >> > currentList.get(0),
>>> >> >> > newPoint);
>>> >> >> > -        // Verify that none of the points beyond the new point in
>>> >> >> > the
>>> >> >> > list are inside the polygon we'd
>>> >> >> > -        // be creating if we stopped making the current polygon
>>> >> >> > right
>>> >> >> > now.
>>> >> >> > -        boolean pointInside = false;
>>> >> >> > -        for (int j = i + 1; j < pointsList.size() - 2; j++) {
>>> >> >> > -          GeoPoint checkPoint = pointsList.get(getLegalIndex(j +
>>> >> >> > endPointIndex + 1, pointsList.size()));
>>> >> >> > -          boolean isInside = true;
>>> >> >> > -          if (isInside && !newBoundary.isWithin(checkPoint))
>>> >> >> > -            isInside = false;
>>> >> >> > -          if (isInside && !returnBoundary.isWithin(checkPoint))
>>> >> >> > -            isInside = false;
>>> >> >> > -          if (isInside) {
>>> >> >> > -            for (SidedPlane plane : currentPlanes) {
>>> >> >> > -              if (!plane.isWithin(checkPoint)) {
>>> >> >> > -                isInside = false;
>>> >> >> > +    // Starting state:
>>> >> >> > +    // The stopping point
>>> >> >> > +    Edge stoppingPoint = edgeBuffer.pickOne();
>>> >> >> > +    // The current edge
>>> >> >> > +    Edge currentEdge = stoppingPoint;
>>> >> >> > +
>>> >> >> > +    // Progressively look for convex sections.  If we find one,
>>> >> >> > we
>>> >> >> > emit
>>> >> >> > it and replace it.
>>> >> >> > +    // Keep going until we have been around once and nothing
>>> >> >> > needed
>>> >> >> > to
>>> >> >> > change, and then
>>> >> >> > +    // do the concave polygon, if necessary.
>>> >> >> > +    while (true) {
>>> >> >> > +
>>> >> >> > +      if (currentEdge == null) {
>>> >> >> > +        // We're done!
>>> >> >> > +        break;
>>> >> >> > +      }
>>> >> >> > +
>>> >> >> > +      // Find convexity around the current edge, if any
>>> >> >> > +      final Boolean foundIt = findConvexPolygon(planetModel,
>>> >> >> > currentEdge, rval, edgeBuffer, holes, testPoint);
>>> >> >> > +      if (foundIt == null) {
>>> >> >> > +        return null;
>>> >> >> > +      }
>>> >> >> > +
>>> >> >> > +      if (foundIt) {
>>> >> >> > +        // New start point
>>> >> >> > +        stoppingPoint = edgeBuffer.pickOne();
>>> >> >> > +        currentEdge = stoppingPoint;
>>> >> >> > +        // back around
>>> >> >> > +        continue;
>>> >> >> > +      }
>>> >> >> > +
>>> >> >> > +      // Otherwise, go on to the next
>>> >> >> > +      currentEdge = edgeBuffer.getNext(currentEdge);
>>> >> >> > +      if (currentEdge == stoppingPoint) {
>>> >> >> > +        break;
>>> >> >> > +      }
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    // If there's anything left in the edge buffer, convert to
>>> >> >> > concave
>>> >> >> > polygon.
>>> >> >> > +    if (makeConcavePolygon(planetModel, rval, edgeBuffer, holes,
>>> >> >> > testPoint) == false) {
>>> >> >> > +      return null;
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    return rval;
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  /** Look for a concave polygon in the remainder of the
>>> >> >> > edgebuffer.
>>> >> >> > +   * By this point, if there are any edges in the edgebuffer,
>>> >> >> > they
>>> >> >> > represent a concave polygon.
>>> >> >> > +   * @param planetModel is the planet model.
>>> >> >> > +   * @param rval is the composite polygon we're building.
>>> >> >> > +   * @param edgeBuffer is the edge buffer.
>>> >> >> > +   * @param holes is the optional list of holes.
>>> >> >> > +   * @param testPoint is the optional test point.
>>> >> >> > +   * @return true unless the testPoint caused failure.
>>> >> >> > +   */
>>> >> >> > +  protected static boolean makeConcavePolygon(final PlanetModel
>>> >> >> > planetModel,
>>> >> >> > +    final GeoCompositePolygon rval,
>>> >> >> > +    final EdgeBuffer edgeBuffer,
>>> >> >> > +    final List<GeoPolygon> holes,
>>> >> >> > +    final GeoPoint testPoint) {
>>> >> >> > +    if (edgeBuffer.size() == 0) {
>>> >> >> > +      return true;
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    // If there are less than three edges, something got messed
>>> >> >> > up
>>> >> >> > somehow.  Don't know how this
>>> >> >> > +    // can happen but check.
>>> >> >> > +    if (edgeBuffer.size() < 3) {
>>> >> >> > +      throw new IllegalStateException("Ending edge buffer had
>>> >> >> > only
>>> >> >> > "+edgeBuffer.size()+" edges");
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    // Create the list of points
>>> >> >> > +    final List<GeoPoint> points = new
>>> >> >> > ArrayList<GeoPoint>(edgeBuffer.size());
>>> >> >> > +    final BitSet internalEdges = new BitSet(edgeBuffer.size()-1);
>>> >> >> > +
>>> >> >> > +    Edge edge = edgeBuffer.pickOne();
>>> >> >> > +    boolean isInternal = edge.isInternal;
>>> >> >> > +    for (int i = 0; i < edgeBuffer.size(); i++) {
>>> >> >> > +      points.add(edge.startPoint);
>>> >> >> > +      if (i < edgeBuffer.size() - 1) {
>>> >> >> > +        internalEdges.set(i, isInternal);
>>> >> >> > +      }
>>> >> >> > +      edge = edgeBuffer.getNext(edge);
>>> >> >> > +      isInternal = edge.isInternal;
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    if (testPoint != null && holes != null && holes.size() > 0) {
>>> >> >> > +      // No holes, for test
>>> >> >> > +      final GeoPolygon testPolygon = new
>>> >> >> > GeoConcavePolygon(planetModel,
>>> >> >> > points, null, internalEdges, isInternal);
>>> >> >> > +      if (testPolygon.isWithin(testPoint)) {
>>> >> >> > +        return false;
>>> >> >> > +      }
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    final GeoPolygon realPolygon = new
>>> >> >> > GeoConcavePolygon(planetModel,
>>> >> >> > points, holes, internalEdges, isInternal);
>>> >> >> > +    if (testPoint != null && (holes == null || holes.size() ==
>>> >> >> > 0)) {
>>> >> >> > +      if (realPolygon.isWithin(testPoint)) {
>>> >> >> > +        return false;
>>> >> >> > +      }
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    rval.addShape(realPolygon);
>>> >> >> > +    return true;
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  /** Look for a convex polygon at the specified edge.  If we
>>> >> >> > find
>>> >> >> > it,
>>> >> >> > create one and adjust the edge buffer.
>>> >> >> > +   * @param planetModel is the planet model.
>>> >> >> > +   * @param currentEdge is the current edge to use starting the
>>> >> >> > search.
>>> >> >> > +   * @param rval is the composite polygon to build.
>>> >> >> > +   * @param edgeBuffer is the edge buffer.
>>> >> >> > +   * @param holes is the optional list of holes.
>>> >> >> > +   * @param testPoint is the optional test point.
>>> >> >> > +   * @return null if the testPoint is within any polygon
>>> >> >> > detected,
>>> >> >> > otherwise true if a convex polygon was created.
>>> >> >> > +   */
>>> >> >> > +  protected static Boolean findConvexPolygon(final PlanetModel
>>> >> >> > planetModel,
>>> >> >> > +    final Edge currentEdge,
>>> >> >> > +    final GeoCompositePolygon rval,
>>> >> >> > +    final EdgeBuffer edgeBuffer,
>>> >> >> > +    final List<GeoPolygon> holes,
>>> >> >> > +    final GeoPoint testPoint) {
>>> >> >> > +
>>> >> >> > +    //System.out.println("Looking at edge "+currentEdge+" with
>>> >> >> > startpoint "+currentEdge.startPoint+" endpoint
>>> >> >> > "+currentEdge.endPoint);
>>> >> >> > +
>>> >> >> > +    // Initialize the structure.
>>> >> >> > +    // We don't keep track of order here; we just care about
>>> >> >> > membership.
>>> >> >> > +    // The only exception is the head and tail pointers.
>>> >> >> > +    final Set<Edge> includedEdges = new HashSet<>();
>>> >> >> > +    includedEdges.add(currentEdge);
>>> >> >> > +    Edge firstEdge = currentEdge;
>>> >> >> > +    Edge lastEdge = currentEdge;
>>> >> >> > +
>>> >> >> > +    // First, walk towards the end until we need to stop
>>> >> >> > +    while (true) {
>>> >> >> > +      if (firstEdge.startPoint == lastEdge.endPoint) {
>>> >> >> > +        break;
>>> >> >> > +      }
>>> >> >> > +      final Edge newLastEdge = edgeBuffer.getNext(lastEdge);
>>> >> >> > +      if (isWithin(newLastEdge.endPoint, includedEdges)) {
>>> >> >> > +        //System.out.println(" maybe can extend to next edge");
>>> >> >> > +        // Found a candidate for extension.  But do some other
>>> >> >> > checks
>>> >> >> > first.  Basically, we need to know if we construct a polygon
>>> >> >> > +        // here will overlap with other remaining points?
>>> >> >> > +        final SidedPlane returnBoundary;
>>> >> >> > +        if (firstEdge.startPoint != newLastEdge.endPoint) {
>>> >> >> > +          returnBoundary = new SidedPlane(firstEdge.endPoint,
>>> >> >> > firstEdge.startPoint, newLastEdge.endPoint);
>>> >> >> > +        } else {
>>> >> >> > +          returnBoundary = null;
>>> >> >> > +        }
>>> >> >> > +        // The complete set of sided planes for the tentative new
>>> >> >> > polygon include the ones in includedEdges, plus the one from
>>> >> >> > newLastEdge,
>>> >> >> > +        // plus the new tentative return boundary.  We have to
>>> >> >> > make
>>> >> >> > sure there are no points from elsewhere within the tentative
>>> >> >> > convex
>>> >> >> > polygon.
>>> >> >> > +        boolean foundPointInside = false;
>>> >> >> > +        final Iterator<Edge> edgeIterator =
>>> >> >> > edgeBuffer.iterator();
>>> >> >> > +        while (edgeIterator.hasNext()) {
>>> >> >> > +          final Edge edge = edgeIterator.next();
>>> >> >> > +          if (!includedEdges.contains(edge) && edge !=
>>> >> >> > newLastEdge)
>>> >> >> > {
>>> >> >> > +            // This edge has a point to check
>>> >> >> > +            if (edge.startPoint != newLastEdge.endPoint) {
>>> >> >> > +              // look at edge.startPoint
>>> >> >> > +              if (isWithin(edge.startPoint, includedEdges,
>>> >> >> > newLastEdge,
>>> >> >> > returnBoundary)) {
>>> >> >> > +                //System.out.println("  nope; point within found:
>>> >> >> > "+edge.startPoint);
>>> >> >> > +                foundPointInside = true;
>>> >> >> > +                break;
>>> >> >> > +              }
>>> >> >> > +            }
>>> >> >> > +            if (edge.endPoint != firstEdge.startPoint) {
>>> >> >> > +              // look at edge.endPoint
>>> >> >> > +              if (isWithin(edge.endPoint, includedEdges,
>>> >> >> > newLastEdge,
>>> >> >> > returnBoundary)) {
>>> >> >> > +                //System.out.println("  nope; point within found:
>>> >> >> > "+edge.endPoint);
>>> >> >> > +                foundPointInside = true;
>>> >> >> >                  break;
>>> >> >> >                }
>>> >> >> >              }
>>> >> >> >            }
>>> >> >> > -          if (isInside) {
>>> >> >> > -            pointInside = true;
>>> >> >> > -            break;
>>> >> >> > -          }
>>> >> >> >          }
>>> >> >> > -        if (!pointInside) {
>>> >> >> > -          // Any excluded points?
>>> >> >> > -          boolean isInternalBoundary = recursionList.size() > 0;
>>> >> >> > -          if (isInternalBoundary) {
>>> >> >> > -            // Handle exclusion
>>> >> >> > -            recursionList.add(newPoint);
>>> >> >> > -            recursionList.add(currentList.get(currentList.size()
>>> >> >> > -
>>> >> >> > 1));
>>> >> >> > -            if (recursionList.size() == pointsList.size()) {
>>> >> >> > -              // We are trying to recurse with a list the same
>>> >> >> > size
>>> >> >> > as
>>> >> >> > the one we started with.
>>> >> >> > -              // Clearly, the polygon cannot be constructed
>>> >> >> > -              throw new IllegalArgumentException("Polygon is
>>> >> >> > illegal;
>>> >> >> > cannot be decomposed into convex parts");
>>> >> >> > +
>>> >> >> > +        if (!foundPointInside) {
>>> >> >> > +          //System.out.println("  extending!");
>>> >> >> > +          // Extend the polygon by the new last edge
>>> >> >> > +          includedEdges.add(newLastEdge);
>>> >> >> > +          lastEdge = newLastEdge;
>>> >> >> > +          // continue extending in this direction
>>> >> >> > +          continue;
>>> >> >> > +        }
>>> >> >> > +      }
>>> >> >> > +      // We can't extend any more in this direction, so break
>>> >> >> > from
>>> >> >> > the
>>> >> >> > loop.
>>> >> >> > +      break;
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    // Now, walk towards the beginning until we need to stop
>>> >> >> > +    while (true) {
>>> >> >> > +      if (firstEdge.startPoint == lastEdge.endPoint) {
>>> >> >> > +        break;
>>> >> >> > +      }
>>> >> >> > +      final Edge newFirstEdge =
>>> >> >> > edgeBuffer.getPrevious(firstEdge);
>>> >> >> > +      if (isWithin(newFirstEdge.startPoint, includedEdges)) {
>>> >> >> > +        //System.out.println(" maybe can extend to previous
>>> >> >> > edge");
>>> >> >> > +        // Found a candidate for extension.  But do some other
>>> >> >> > checks
>>> >> >> > first.  Basically, we need to know if we construct a polygon
>>> >> >> > +        // here will overlap with other remaining points?
>>> >> >> > +        final SidedPlane returnBoundary;
>>> >> >> > +        if (newFirstEdge.startPoint != lastEdge.endPoint) {
>>> >> >> > +          returnBoundary = new SidedPlane(lastEdge.startPoint,
>>> >> >> > lastEdge.endPoint, newFirstEdge.startPoint);
>>> >> >> > +        } else {
>>> >> >> > +          returnBoundary = null;
>>> >> >> > +        }
>>> >> >> > +        // The complete set of sided planes for the tentative new
>>> >> >> > polygon include the ones in includedEdges, plus the one from
>>> >> >> > newLastEdge,
>>> >> >> > +        // plus the new tentative return boundary.  We have to
>>> >> >> > make
>>> >> >> > sure there are no points from elsewhere within the tentative
>>> >> >> > convex
>>> >> >> > polygon.
>>> >> >> > +        boolean foundPointInside = false;
>>> >> >> > +        final Iterator<Edge> edgeIterator =
>>> >> >> > edgeBuffer.iterator();
>>> >> >> > +        while (edgeIterator.hasNext()) {
>>> >> >> > +          final Edge edge = edgeIterator.next();
>>> >> >> > +          if (!includedEdges.contains(edge) && edge !=
>>> >> >> > newFirstEdge)
>>> >> >> > {
>>> >> >> > +            // This edge has a point to check
>>> >> >> > +            if (edge.startPoint != lastEdge.endPoint) {
>>> >> >> > +              // look at edge.startPoint
>>> >> >> > +              if (isWithin(edge.startPoint, includedEdges,
>>> >> >> > newFirstEdge, returnBoundary)) {
>>> >> >> > +                //System.out.println("  nope; point within found:
>>> >> >> > "+edge.startPoint);
>>> >> >> > +                foundPointInside = true;
>>> >> >> > +                break;
>>> >> >> > +              }
>>> >> >> > +            }
>>> >> >> > +            if (edge.endPoint != newFirstEdge.startPoint) {
>>> >> >> > +              // look at edge.endPoint
>>> >> >> > +              if (isWithin(edge.endPoint, includedEdges,
>>> >> >> > newFirstEdge,
>>> >> >> > returnBoundary)) {
>>> >> >> > +                //System.out.println("  nope; point within found:
>>> >> >> > "+edge.endPoint);
>>> >> >> > +                foundPointInside = true;
>>> >> >> > +                break;
>>> >> >> > +              }
>>> >> >> >              }
>>> >> >> > -            // We want the other side for the recursion
>>> >> >> > -            SidedPlane otherSideNewBoundary = new
>>> >> >> > SidedPlane(newBoundary);
>>> >> >> > -            rval.addShape(buildPolygonShape(planetModel,
>>> >> >> > recursionList,
>>> >> >> > recursionList.size() - 2, recursionList.size() - 1,
>>> >> >> > otherSideNewBoundary,
>>> >> >> > true));
>>> >> >> > -            recursionList.clear();
>>> >> >> >            }
>>> >> >> > -          currentList.add(newPoint);
>>> >> >> > -          internalEdgeList.set(currentPlanes.size(),
>>> >> >> > isInternalBoundary);
>>> >> >> > -          currentPlanes.add(newBoundary);
>>> >> >> > -        } else {
>>> >> >> > -          recursionList.add(newPoint);
>>> >> >> >          }
>>> >> >> > -      } else {
>>> >> >> > -        recursionList.add(newPoint);
>>> >> >> > +
>>> >> >> > +        if (!foundPointInside) {
>>> >> >> > +          //System.out.println("  extending!");
>>> >> >> > +          // Extend the polygon by the new last edge
>>> >> >> > +          includedEdges.add(newFirstEdge);
>>> >> >> > +          firstEdge = newFirstEdge;
>>> >> >> > +          // continue extending in this direction
>>> >> >> > +          continue;
>>> >> >> > +        }
>>> >> >> >        }
>>> >> >> > +      // We can't extend any more in this direction, so break
>>> >> >> > from
>>> >> >> > the
>>> >> >> > loop.
>>> >> >> > +      break;
>>> >> >> >      }
>>> >> >> >
>>> >> >> > -    boolean returnEdgeInternalBoundary = recursionList.size() >
>>> >> >> > 0;
>>> >> >> > -    if (returnEdgeInternalBoundary) {
>>> >> >> > -      // The last step back to the start point had a recursion,
>>> >> >> > so
>>> >> >> > take
>>> >> >> > care of that before we complete our work
>>> >> >> > -      recursionList.add(currentList.get(0));
>>> >> >> > -      recursionList.add(currentList.get(currentList.size() - 1));
>>> >> >> > -      if (recursionList.size() == pointsList.size()) {
>>> >> >> > -        // We are trying to recurse with a list the same size as
>>> >> >> > the
>>> >> >> > one we started with.
>>> >> >> > -        // Clearly, the polygon cannot be constructed
>>> >> >> > -        throw new IllegalArgumentException("Polygon is illegal;
>>> >> >> > cannot
>>> >> >> > be decomposed into convex parts");
>>> >> >> > +    // Ok, figure out what we've accumulated.  If it is enough
>>> >> >> > for a
>>> >> >> > polygon, build it.
>>> >> >> > +    if (includedEdges.size() < 2) {
>>> >> >> > +      //System.out.println("Done edge "+currentEdge+": no poly
>>> >> >> > found");
>>> >> >> > +      return false;
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    // It's enough to build a convex polygon
>>> >> >> > +    //System.out.println("Edge "+currentEdge+": Found complex
>>> >> >> > poly");
>>> >> >> > +
>>> >> >> > +    // Create the point list and edge list, starting with the
>>> >> >> > first
>>> >> >> > edge and going to the last.  The return edge will be between
>>> >> >> > +    // the start point of the first edge and the end point of the
>>> >> >> > last
>>> >> >> > edge.  If the first edge start point is the same as the last edge
>>> >> >> > end
>>> >> >> > point,
>>> >> >> > +    // it's a degenerate case and we want to just clean out the
>>> >> >> > edge
>>> >> >> > buffer entirely.
>>> >> >> > +
>>> >> >> > +    final List<GeoPoint> points = new
>>> >> >> > ArrayList<GeoPoint>(includedEdges.size());
>>> >> >> > +    final BitSet internalEdges = new
>>> >> >> > BitSet(includedEdges.size()-1);
>>> >> >> > +    final boolean returnIsInternal;
>>> >> >> > +
>>> >> >> > +    if (firstEdge.startPoint == lastEdge.endPoint) {
>>> >> >> > +      // Degenerate case!!  There is no return edge -- or rather,
>>> >> >> > we
>>> >> >> > already have it.
>>> >> >> > +      Edge edge = firstEdge;
>>> >> >> > +      points.add(edge.startPoint);
>>> >> >> > +      int i = 0;
>>> >> >> > +      while (true) {
>>> >> >> > +        if (edge == lastEdge) {
>>> >> >> > +          break;
>>> >> >> > +        }
>>> >> >> > +        points.add(edge.endPoint);
>>> >> >> > +        internalEdges.set(i++, edge.isInternal);
>>> >> >> > +        edge = edgeBuffer.getNext(edge);
>>> >> >> >        }
>>> >> >> > -      // Construct a sided plane based on these two points, and
>>> >> >> > the
>>> >> >> > previous point
>>> >> >> > -      SidedPlane newBoundary = new
>>> >> >> > SidedPlane(currentList.get(currentList.size() - 2),
>>> >> >> > currentList.get(0),
>>> >> >> > currentList.get(currentList.size() - 1));
>>> >> >> > -      // We want the other side for the recursion
>>> >> >> > -      SidedPlane otherSideNewBoundary = new
>>> >> >> > SidedPlane(newBoundary);
>>> >> >> > -      rval.addShape(buildPolygonShape(planetModel, recursionList,
>>> >> >> > recursionList.size() - 2, recursionList.size() - 1,
>>> >> >> > otherSideNewBoundary,
>>> >> >> > true));
>>> >> >> > -      recursionList.clear();
>>> >> >> > +      returnIsInternal = lastEdge.isInternal;
>>> >> >> > +      edgeBuffer.clear();
>>> >> >> > +    } else {
>>> >> >> > +      // Build the return edge (internal, of course)
>>> >> >> > +      final SidedPlane returnSidedPlane = new
>>> >> >> > SidedPlane(firstEdge.endPoint, false, firstEdge.startPoint,
>>> >> >> > lastEdge.endPoint);
>>> >> >> > +      final Edge returnEdge = new Edge(firstEdge.startPoint,
>>> >> >> > lastEdge.endPoint, returnSidedPlane, true);
>>> >> >> > +
>>> >> >> > +      // Build point list and edge list
>>> >> >> > +      final List<Edge> edges = new
>>> >> >> > ArrayList<Edge>(includedEdges.size());
>>> >> >> > +      returnIsInternal = true;
>>> >> >> > +
>>> >> >> > +      Edge edge = firstEdge;
>>> >> >> > +      points.add(edge.startPoint);
>>> >> >> > +      int i = 0;
>>> >> >> > +      while (true) {
>>> >> >> > +        points.add(edge.endPoint);
>>> >> >> > +        internalEdges.set(i++, edge.isInternal);
>>> >> >> > +        edges.add(edge);
>>> >> >> > +        if (edge == lastEdge) {
>>> >> >> > +          break;
>>> >> >> > +        }
>>> >> >> > +        edge = edgeBuffer.getNext(edge);
>>> >> >> > +      }
>>> >> >> > +
>>> >> >> > +      // Modify the edge buffer
>>> >> >> > +      edgeBuffer.replace(edges, returnEdge);
>>> >> >> >      }
>>> >> >> > -    // Now, add in the current shape.
>>> >> >> > -    rval.addShape(new GeoConvexPolygon(planetModel, currentList,
>>> >> >> > internalEdgeList, returnEdgeInternalBoundary));
>>> >> >> > -    //System.out.println("Done creating polygon");
>>> >> >> > -    return rval;
>>> >> >> > +
>>> >> >> > +    // Now, construct the polygon
>>> >> >> > +    if (testPoint != null && holes != null && holes.size() > 0) {
>>> >> >> > +      // No holes, for test
>>> >> >> > +      final GeoPolygon testPolygon = new
>>> >> >> > GeoConvexPolygon(planetModel,
>>> >> >> > points, null, internalEdges, returnIsInternal);
>>> >> >> > +      if (testPolygon.isWithin(testPoint)) {
>>> >> >> > +        return null;
>>> >> >> > +      }
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    final GeoPolygon realPolygon = new
>>> >> >> > GeoConvexPolygon(planetModel,
>>> >> >> > points, holes, internalEdges, returnIsInternal);
>>> >> >> > +    if (testPoint != null && (holes == null || holes.size() ==
>>> >> >> > 0)) {
>>> >> >> > +      if (realPolygon.isWithin(testPoint)) {
>>> >> >> > +        return null;
>>> >> >> > +      }
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    rval.addShape(realPolygon);
>>> >> >> > +    return true;
>>> >> >> >    }
>>> >> >> > -
>>> >> >> > -  /** Check if a point is within a described list of planes.
>>> >> >> > -   *@param newPoint is the point.
>>> >> >> > -   *@param currentPlanes is the list of planes.
>>> >> >> > -   *@return true if within.
>>> >> >> > -   */
>>> >> >> > -  protected static boolean isWithin(final GeoPoint newPoint,
>>> >> >> > final
>>> >> >> > List<SidedPlane> currentPlanes) {
>>> >> >> > -    for (SidedPlane p : currentPlanes) {
>>> >> >> > -      if (!p.isWithin(newPoint))
>>> >> >> > +
>>> >> >> > +  protected static boolean isWithin(final GeoPoint point, final
>>> >> >> > Set<Edge> edgeSet, final Edge extension, final SidedPlane
>>> >> >> > returnBoundary) {
>>> >> >> > +    if (!extension.plane.isWithin(point)) {
>>> >> >> > +      return false;
>>> >> >> > +    }
>>> >> >> > +    if (returnBoundary != null &&
>>> >> >> > !returnBoundary.isWithin(point)) {
>>> >> >> > +      return false;
>>> >> >> > +    }
>>> >> >> > +    return isWithin(point, edgeSet);
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  protected static boolean isWithin(final GeoPoint point, final
>>> >> >> > Set<Edge> edgeSet) {
>>> >> >> > +    for (final Edge edge : edgeSet) {
>>> >> >> > +      if (!edge.plane.isWithin(point)) {
>>> >> >> >          return false;
>>> >> >> > +      }
>>> >> >> >      }
>>> >> >> >      return true;
>>> >> >> >    }
>>> >> >> > -
>>> >> >> > +
>>> >> >> >    /** Convert raw point index into valid array position.
>>> >> >> >     *@param index is the array index.
>>> >> >> >     *@param size is the array size.
>>> >> >> > @@ -184,4 +671,174 @@ public class GeoPolygonFactory {
>>> >> >> >      return index;
>>> >> >> >    }
>>> >> >> >
>>> >> >> > +  /** Class representing a single (unused) edge.
>>> >> >> > +   */
>>> >> >> > +  protected static class Edge {
>>> >> >> > +    public final SidedPlane plane;
>>> >> >> > +    public final GeoPoint startPoint;
>>> >> >> > +    public final GeoPoint endPoint;
>>> >> >> > +    public final boolean isInternal;
>>> >> >> > +
>>> >> >> > +    public Edge(final GeoPoint startPoint, final GeoPoint
>>> >> >> > endPoint,
>>> >> >> > final SidedPlane plane, final boolean isInternal) {
>>> >> >> > +      this.startPoint = startPoint;
>>> >> >> > +      this.endPoint = endPoint;
>>> >> >> > +      this.plane = plane;
>>> >> >> > +      this.isInternal = isInternal;
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    @Override
>>> >> >> > +    public int hashCode() {
>>> >> >> > +      return System.identityHashCode(this);
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    @Override
>>> >> >> > +    public boolean equals(final Object o) {
>>> >> >> > +      return o == this;
>>> >> >> > +    }
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  /** Class representing a pool of unused edges, all linked
>>> >> >> > together
>>> >> >> > by
>>> >> >> > vertices.
>>> >> >> > +   */
>>> >> >> > +  protected static class EdgeBuffer {
>>> >> >> > +    protected final Set<Edge> edges = new HashSet<>();
>>> >> >> > +    protected final Map<Edge, Edge> previousEdges = new
>>> >> >> > HashMap<>();
>>> >> >> > +    protected final Map<Edge, Edge> nextEdges = new HashMap<>();
>>> >> >> > +
>>> >> >> > +    public EdgeBuffer(final List<GeoPoint> pointList, final int
>>> >> >> > startPlaneStartIndex, final int startPlaneEndIndex, final
>>> >> >> > SidedPlane
>>> >> >> > startPlane, final boolean startPlaneIsInternal) {
>>> >> >> > +      /*
>>> >> >> > +      System.out.println("Initial points:");
>>> >> >> > +      for (final GeoPoint p : pointList) {
>>> >> >> > +        System.out.println(" "+p);
>>> >> >> > +      }
>>> >> >> > +      System.out.println("For start plane, the following points
>>> >> >> > are
>>> >> >> > in/out:");
>>> >> >> > +      for (final GeoPoint p: pointList) {
>>> >> >> > +        System.out.println(" "+p+" is:
>>> >> >> > "+(startPlane.isWithin(p)?"in":"out"));
>>> >> >> > +      }
>>> >> >> > +      */
>>> >> >> > +
>>> >> >> > +      final Edge startEdge = new
>>> >> >> > Edge(pointList.get(startPlaneStartIndex),
>>> >> >> > pointList.get(startPlaneEndIndex),
>>> >> >> > startPlane, startPlaneIsInternal);
>>> >> >> > +      // Fill in the EdgeBuffer by walking around creating more
>>> >> >> > stuff
>>> >> >> > +      Edge currentEdge = startEdge;
>>> >> >> > +      int startIndex = startPlaneStartIndex;
>>> >> >> > +      int endIndex = startPlaneEndIndex;
>>> >> >> > +      boolean isInternal = startPlaneIsInternal;
>>> >> >> > +      while (true) {
>>> >> >> > +        // Compute the next edge
>>> >> >> > +        startIndex = endIndex;
>>> >> >> > +        endIndex++;
>>> >> >> > +        if (endIndex >= pointList.size()) {
>>> >> >> > +          endIndex -= pointList.size();
>>> >> >> > +        }
>>> >> >> > +        // Get the next point
>>> >> >> > +        final GeoPoint newPoint = pointList.get(endIndex);
>>> >> >> > +        // Build the new edge
>>> >> >> > +        final boolean isNewPointWithin =
>>> >> >> > currentEdge.plane.isWithin(newPoint);
>>> >> >> > +        final SidedPlane newPlane = new
>>> >> >> > SidedPlane(currentEdge.startPoint, isNewPointWithin,
>>> >> >> > pointList.get(startIndex), newPoint);
>>> >> >> > +        /*
>>> >> >> > +        System.out.println("For next plane, the following points
>>> >> >> > are
>>> >> >> > in/out:");
>>> >> >> > +        for (final GeoPoint p: pointList) {
>>> >> >> > +          System.out.println(" "+p+" is:
>>> >> >> > "+(newPlane.isWithin(p)?"in":"out"));
>>> >> >> > +        }
>>> >> >> > +        */
>>> >> >> > +        final Edge newEdge = new Edge(pointList.get(startIndex),
>>> >> >> > pointList.get(endIndex), newPlane, false);
>>> >> >> > +
>>> >> >> > +        // Link it in
>>> >> >> > +        previousEdges.put(newEdge, currentEdge);
>>> >> >> > +        nextEdges.put(currentEdge, newEdge);
>>> >> >> > +        edges.add(newEdge);
>>> >> >> > +        currentEdge = newEdge;
>>> >> >> > +
>>> >> >> > +        if (currentEdge.endPoint == startEdge.startPoint) {
>>> >> >> > +          // We finish here.  Link the current edge to the start
>>> >> >> > edge,
>>> >> >> > and exit
>>> >> >> > +          previousEdges.put(startEdge, currentEdge);
>>> >> >> > +          nextEdges.put(currentEdge, startEdge);
>>> >> >> > +          edges.add(startEdge);
>>> >> >> > +          break;
>>> >> >> > +        }
>>> >> >> > +      }
>>> >> >> > +
>>> >> >> > +      // Verify the structure.
>>> >> >> > +      //verify();
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    /*
>>> >> >> > +    protected void verify() {
>>> >> >> > +      if (edges.size() != previousEdges.size() || edges.size() !=
>>> >> >> > nextEdges.size()) {
>>> >> >> > +        throw new IllegalStateException("broken structure");
>>> >> >> > +      }
>>> >> >> > +      // Confirm each edge
>>> >> >> > +      for (final Edge e : edges) {
>>> >> >> > +        final Edge previousEdge = getPrevious(e);
>>> >> >> > +        final Edge nextEdge = getNext(e);
>>> >> >> > +        if (e.endPoint != nextEdge.startPoint) {
>>> >> >> > +          throw new IllegalStateException("broken structure");
>>> >> >> > +        }
>>> >> >> > +        if (e.startPoint != previousEdge.endPoint) {
>>> >> >> > +          throw new IllegalStateException("broken structure");
>>> >> >> > +        }
>>> >> >> > +        if (getNext(previousEdge) != e) {
>>> >> >> > +          throw new IllegalStateException("broken structure");
>>> >> >> > +        }
>>> >> >> > +        if (getPrevious(nextEdge) != e) {
>>> >> >> > +          throw new IllegalStateException("broken structure");
>>> >> >> > +        }
>>> >> >> > +      }
>>> >> >> > +    }
>>> >> >> > +    */
>>> >> >> > +
>>> >> >> > +    public Edge getPrevious(final Edge currentEdge) {
>>> >> >> > +      return previousEdges.get(currentEdge);
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    public Edge getNext(final Edge currentEdge) {
>>> >> >> > +      return nextEdges.get(currentEdge);
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    public void replace(final List<Edge> removeList, final Edge
>>> >> >> > newEdge) {
>>> >> >> > +      /*
>>> >> >> > +      System.out.println("Replacing: ");
>>> >> >> > +      for (final Edge e : removeList) {
>>> >> >> > +        System.out.println(" "+e.startPoint+"-->"+e.endPoint);
>>> >> >> > +      }
>>> >> >> > +      System.out.println("...with:
>>> >> >> > "+newEdge.startPoint+"-->"+newEdge.endPoint);
>>> >> >> > +      */
>>> >> >> > +      final Edge previous = previousEdges.get(removeList.get(0));
>>> >> >> > +      final Edge next =
>>> >> >> > nextEdges.get(removeList.get(removeList.size()-1));
>>> >> >> > +      edges.add(newEdge);
>>> >> >> > +      previousEdges.put(newEdge, previous);
>>> >> >> > +      nextEdges.put(previous, newEdge);
>>> >> >> > +      previousEdges.put(next, newEdge);
>>> >> >> > +      nextEdges.put(newEdge, next);
>>> >> >> > +      for (final Edge edge : removeList) {
>>> >> >> > +        edges.remove(edge);
>>> >> >> > +        previousEdges.remove(edge);
>>> >> >> > +        nextEdges.remove(edge);
>>> >> >> > +      }
>>> >> >> > +      //verify();
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    public void clear() {
>>> >> >> > +      edges.clear();
>>> >> >> > +      previousEdges.clear();
>>> >> >> > +      nextEdges.clear();
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    public int size() {
>>> >> >> > +      return edges.size();
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    public Iterator<Edge> iterator() {
>>> >> >> > +      return edges.iterator();
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +    public Edge pickOne() {
>>> >> >> > +      final Iterator<Edge> iter = edges.iterator();
>>> >> >> > +      if (iter.hasNext()) {
>>> >> >> > +        return iter.next();
>>> >> >> > +      }
>>> >> >> > +      return null;
>>> >> >> > +    }
>>> >> >> > +
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> >  }
>>> >> >> >
>>> >> >> >
>>> >> >> >
>>> >> >> >
>>> >> >> > http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/98e04cf4/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/SidedPlane.java
>>> >> >> >
>>> >> >> >
>>> >> >> > ----------------------------------------------------------------------
>>> >> >> > diff --git
>>> >> >> >
>>> >> >> >
>>> >> >> > a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/SidedPlane.java
>>> >> >> >
>>> >> >> >
>>> >> >> > b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/SidedPlane.java
>>> >> >> > index e080bc0..0c19a9e 100755
>>> >> >> > ---
>>> >> >> >
>>> >> >> >
>>> >> >> > a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/SidedPlane.java
>>> >> >> > +++
>>> >> >> >
>>> >> >> >
>>> >> >> > b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/SidedPlane.java
>>> >> >> > @@ -31,7 +31,7 @@ public class SidedPlane extends Plane implements
>>> >> >> > Membership {
>>> >> >> >     *
>>> >> >> >     * @param sidedPlane is the existing plane.
>>> >> >> >     */
>>> >> >> > -  public SidedPlane(SidedPlane sidedPlane) {
>>> >> >> > +  public SidedPlane(final SidedPlane sidedPlane) {
>>> >> >> >      super(sidedPlane, sidedPlane.D);
>>> >> >> >      this.sigNum = -sidedPlane.sigNum;
>>> >> >> >    }
>>> >> >> > @@ -44,7 +44,7 @@ public class SidedPlane extends Plane implements
>>> >> >> > Membership {
>>> >> >> >     * @param A is the first in-plane point
>>> >> >> >     * @param B is the second in-plane point
>>> >> >> >     */
>>> >> >> > -  public SidedPlane(Vector p, Vector A, Vector B) {
>>> >> >> > +  public SidedPlane(final Vector p, final Vector A, final Vector
>>> >> >> > B)
>>> >> >> > {
>>> >> >> >      super(A, B);
>>> >> >> >      sigNum = Math.signum(evaluate(p));
>>> >> >> >      if (sigNum == 0.0)
>>> >> >> > @@ -52,6 +52,22 @@ public class SidedPlane extends Plane
>>> >> >> > implements
>>> >> >> > Membership {
>>> >> >> >    }
>>> >> >> >
>>> >> >> >    /**
>>> >> >> > +   * Construct a sided plane from a pair of vectors describing
>>> >> >> > points,
>>> >> >> > and including
>>> >> >> > +   * origin, plus a point p which describes the side.
>>> >> >> > +   *
>>> >> >> > +   * @param p point to evaluate
>>> >> >> > +   * @param onSide is true if the point is on the correct side of
>>> >> >> > the
>>> >> >> > plane, false otherwise.
>>> >> >> > +   * @param A is the first in-plane point
>>> >> >> > +   * @param B is the second in-plane point
>>> >> >> > +   */
>>> >> >> > +  public SidedPlane(final Vector p, final boolean onSide, final
>>> >> >> > Vector
>>> >> >> > A, final Vector B) {
>>> >> >> > +    super(A, B);
>>> >> >> > +    sigNum =
>>> >> >> > onSide?Math.signum(evaluate(p)):-Math.signum(evaluate(p));
>>> >> >> > +    if (sigNum == 0.0)
>>> >> >> > +      throw new IllegalArgumentException("Cannot determine
>>> >> >> > sidedness
>>> >> >> > because check point is on plane.");
>>> >> >> > +  }
>>> >> >> > +
>>> >> >> > +  /**
>>> >> >> >     * Construct a sided plane from a point and a Z coordinate.
>>> >> >> >     *
>>> >> >> >     * @param p      point to evaluate.
>>> >> >> >
>>> >> >> >
>>> >> >> >
>>> >> >> >
>>> >> >> > http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/98e04cf4/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 d9b220d..ff9bd90 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
>>> >> >> > @@ -20,6 +20,7 @@ import java.util.ArrayList;
>>> >> >> >  import java.util.List;
>>> >> >> >
>>> >> >> >  import org.junit.Test;
>>> >> >> > +import org.junit.Ignore;
>>> >> >> >
>>> >> >> >  import static org.junit.Assert.assertEquals;
>>> >> >> >  import static org.junit.Assert.assertFalse;
>>> >> >> > @@ -27,10 +28,44 @@ import static org.junit.Assert.assertTrue;
>>> >> >> >
>>> >> >> >  public class GeoPolygonTest {
>>> >> >> >
>>> >> >> > +  @Ignore
>>> >> >> > +  @Test
>>> >> >> > +  public void testPolygonClockwise() {
>>> >> >> > +    GeoPolygon c;
>>> >> >> > +    GeoPoint gp;
>>> >> >> > +    List<GeoPoint> points;
>>> >> >> > +
>>> >> >> > +    // Points go counterclockwise, so
>>> >> >> > +    points = new ArrayList<GeoPoint>();
>>> >> >> > +    points.add(new GeoPoint(PlanetModel.SPHERE, -0.1, -0.5));
>>> >> >> > +    points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.6));
>>> >> >> > +    points.add(new GeoPoint(PlanetModel.SPHERE, 0.1, -0.5));
>>> >> >> > +    points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.4));
>>> >> >> > +
>>> >> >> > +    c = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE,
>>> >> >> > points);
>>> >> >> > +
>>> >> >> > +    // Middle point should NOT be within!!
>>> >> >> > +    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.5);
>>> >> >> > +    assertTrue(!c.isWithin(gp));
>>> >> >> > +
>>> >> >> > +    // Now, go clockwise
>>> >> >> > +    points = new ArrayList<GeoPoint>();
>>> >> >> > +    points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.4));
>>> >> >> > +    points.add(new GeoPoint(PlanetModel.SPHERE, 0.1, -0.5));
>>> >> >> > +    points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.6));
>>> >> >> > +    points.add(new GeoPoint(PlanetModel.SPHERE, -0.1, -0.5));
>>> >> >> > +
>>> >> >> > +    c = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE,
>>> >> >> > points);
>>> >> >> > +
>>> >> >> > +    // Middle point should be within!!
>>> >> >> > +    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.5);
>>> >> >> > +    assertTrue(c.isWithin(gp));
>>> >> >> > +
>>> >> >> > +  }
>>> >> >> >
>>> >> >> >    @Test
>>> >> >> >    public void testPolygonPointWithin() {
>>> >> >> > -    GeoMembershipShape c;
>>> >> >> > +    GeoPolygon c;
>>> >> >> >      GeoPoint gp;
>>> >> >> >      List<GeoPoint> points;
>>> >> >> >
>>> >> >> > @@ -162,4 +197,45 @@ public class GeoPolygonTest {
>>> >> >> >      assertEquals(0.1, b.getMaxLatitude(), 0.000001);
>>> >> >> >    }
>>> >> >> >
>>> >> >> > +  @Test
>>> >> >> > +  public void testPolygonBoundsCase1() {
>>> >> >> > +    GeoPolygon c;
>>> >> >> > +    LatLonBounds b;
>>> >> >> > +    List<GeoPoint> points;
>>> >> >> > +    XYZBounds xyzb;
>>> >> >> > +    GeoPoint point1;
>>> >> >> > +    GeoPoint point2;
>>> >> >> > +    GeoArea area;
>>> >> >> > +
>>> >> >> > +    // Build the polygon
>>> >> >> > +    points = new ArrayList<>();
>>> >> >> > +    points.add(new GeoPoint(PlanetModel.WGS84,
>>> >> >> > 0.7769776943105245,
>>> >> >> > -2.157536559188766));
>>> >> >> > +    points.add(new GeoPoint(PlanetModel.WGS84,
>>> >> >> > -0.9796549195552824,
>>> >> >> > -0.25078026625235256));
>>> >> >> > +    points.add(new GeoPoint(PlanetModel.WGS84,
>>> >> >> > 0.17644522781457245,
>>> >> >> > 2.4225312555674967));
>>> >> >> > +    points.add(new GeoPoint(PlanetModel.WGS84,
>>> >> >> > -1.4459804612164617,
>>> >> >> > -1.2970934639728127));
>>> >> >> > +    c = GeoPolygonFactory.makeGeoPolygon(PlanetModel.WGS84,
>>> >> >> > points,
>>> >> >> > 3);
>>> >> >> > +    System.out.println(c);
>>> >> >> > +    // GeoCompositeMembershipShape: {[GeoConvexPolygon:
>>> >> >> > {planetmodel=PlanetModel.WGS84, points=
>>> >> >> > +    // [[lat=0.17644522781457245, lon=2.4225312555674967],
>>> >> >> > +    //  [lat=-1.4459804612164617, lon=-1.2970934639728127],
>>> >> >> > +    // [lat=0.7769776943105245, lon=-2.157536559188766]]},
>>> >> >> > +    // GeoConcavePolygon: {planetmodel=PlanetModel.WGS84, points=
>>> >> >> > +    // [[lat=-0.9796549195552824, lon=-0.25078026625235256],
>>> >> >> > +    //  [lat=0.17644522781457245, lon=2.4225312555674967],
>>> >> >> > +    //  [lat=0.7769776943105245, lon=-2.157536559188766]]}]}
>>> >> >> > +    point1 = new GeoPoint(PlanetModel.WGS84, -1.2013743680763862,
>>> >> >> > 0.48458963747230094);
>>> >> >> > +    point2 = new GeoPoint(0.3189285805649921,
>>> >> >> > 0.16790264636909197,
>>> >> >> > -0.9308557496413026);
>>> >> >> > +
>>> >> >> > +    assertTrue(c.isWithin(point1));
>>> >> >> > +    assertTrue(c.isWithin(point2));
>>> >> >> > +
>>> >> >> > +    // Now try bounds
>>> >> >> > +    xyzb = new XYZBounds();
>>> >> >> > +    c.getBounds(xyzb);
>>> >> >> > +    area = GeoAreaFactory.makeGeoArea(PlanetModel.WGS84,
>>> >> >> > +      xyzb.getMinimumX(), xyzb.getMaximumX(), xyzb.getMinimumY(),
>>> >> >> > xyzb.getMaximumY(), xyzb.getMinimumZ(), xyzb.getMaximumZ());
>>> >> >> > +
>>> >> >> > +    assertTrue(area.isWithin(point1));
>>> >> >> > +    assertTrue(area.isWithin(point2));
>>> >> >> > +  }
>>> >> >> >  }
>>> >> >> >
>>> >> >>
>>> >> >>
>>> >> >> ---------------------------------------------------------------------
>>> >> >> To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org
>>> >> >> For additional commands, e-mail: dev-h...@lucene.apache.org
>>> >> >>
>>> >> >
>>> >>
>>> >>
>>> >> ---------------------------------------------------------------------
>>> >> To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org
>>> >> For additional commands, e-mail: dev-h...@lucene.apache.org
>>> >>
>>> >
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org
>>> For additional commands, e-mail: dev-h...@lucene.apache.org
>>>
>>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org
> For additional commands, e-mail: dev-h...@lucene.apache.org
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org
For additional commands, e-mail: dev-h...@lucene.apache.org

Reply via email to