Program: 57076
Project: 57076
Buggy source:
/** 
 * Get a spherical cap enclosing the polygon. <p> This method is intended as a first test to quickly identify points that are guaranteed to be outside of the region, hence performing a full  {@link #checkPoint(org.apache.commons.math4.geometry.Vector) checkPoint}only if the point status remains undecided after the quick check. It is is therefore mostly useful to speed up computation for small polygons with complex shapes (say a country boundary on Earth), as the spherical cap will be small and hence will reliably identify a large part of the sphere as outside, whereas the full check can be more computing intensive. A typical use case is therefore: </p> <pre> // compute region, plus an enclosing spherical cap SphericalPolygonsSet complexShape = ...; EnclosingBall<Sphere2D, S2Point> cap = complexShape.getEnclosingCap(); // check lots of points for (Cartesian3D p : points) { final Location l; if (cap.contains(p)) { // we cannot be sure where the point is // we need to perform the full computation l = complexShape.checkPoint(v); } else { // no need to do further computation, // we already know the point is outside l = Location.OUTSIDE; } // use l ... } </pre> <p> In the special cases of empty or whole sphere polygons, special spherical caps are returned, with angular radius set to negative or positive infinity so the   {@link EnclosingBall#contains(org.apache.commons.math4.geometry.Point) ball.contains(point)}method return always false or true. </p> <p> This method is <em>not</em> guaranteed to return the smallest enclosing cap. </p>
 * @return a spherical cap enclosing the polygon
 */
public EnclosingBall<Sphere2D,S2Point> getEnclosingCap(){
  if (isEmpty()) {
    return new EnclosingBall<>(S2Point.PLUS_K,Double.NEGATIVE_INFINITY);
  }
  if (isFull()) {
    return new EnclosingBall<>(S2Point.PLUS_K,Double.POSITIVE_INFINITY);
  }
  final BSPTree<Sphere2D> root=getTree(false);
  if (isEmpty(root.getMinus()) && isFull(root.getPlus())) {
    final Circle circle=(Circle)root.getCut().getHyperplane();
    return new EnclosingBall<>(new S2Point(circle.getPole()).negate(),0.5 * FastMath.PI);
  }
  if (isFull(root.getMinus()) && isEmpty(root.getPlus())) {
    final Circle circle=(Circle)root.getCut().getHyperplane();
    return new EnclosingBall<>(new S2Point(circle.getPole()),0.5 * FastMath.PI);
  }
  final List<Cartesian3D> points=getInsidePoints();
  final List<Vertex> boundary=getBoundaryLoops();
  for (  final Vertex loopStart : boundary) {
    int count=0;
    for (Vertex v=loopStart; count == 0 || v != loopStart; v=v.getOutgoing().getEnd()) {
      ++count;
      points.add(v.getLocation().getVector());
    }
  }
  final SphereGenerator generator=new SphereGenerator();
  final WelzlEncloser<Euclidean3D,Cartesian3D> encloser=new WelzlEncloser<>(getTolerance(),generator);
  EnclosingBall<Euclidean3D,Cartesian3D> enclosing3D=encloser.enclose(points);
  final Cartesian3D[] support3D=enclosing3D.getSupport();
  final double r=enclosing3D.getRadius();
  final double h=enclosing3D.getCenter().getNorm();
  if (h < getTolerance()) {
    EnclosingBall<Sphere2D,S2Point> enclosingS2=new EnclosingBall<>(S2Point.PLUS_K,Double.POSITIVE_INFINITY);
    for (    Cartesian3D outsidePoint : getOutsidePoints()) {
      final S2Point outsideS2=new S2Point(outsidePoint);
      final BoundaryProjection<Sphere2D> projection=projectToBoundary(outsideS2);
      if (FastMath.PI - projection.getOffset() < enclosingS2.getRadius()) {
        enclosingS2=new EnclosingBall<>(outsideS2.negate(),FastMath.PI - projection.getOffset(),(S2Point)projection.getProjected());
      }
    }
    return enclosingS2;
  }
  final S2Point[] support=new S2Point[support3D.length];
  for (int i=0; i < support3D.length; ++i) {
    support[i]=new S2Point(support3D[i]);
  }
  final EnclosingBall<Sphere2D,S2Point> enclosingS2=new EnclosingBall<>(new S2Point(enclosing3D.getCenter()),FastMath.acos((1 + h * h - r * r) / (2 * h)),support);
  return enclosingS2;
}

Fixed version (ground truth):
/** 
 * Get a spherical cap enclosing the polygon. <p> This method is intended as a first test to quickly identify points that are guaranteed to be outside of the region, hence performing a full  {@link #checkPoint(org.apache.commons.math4.geometry.Point) checkPoint}only if the point status remains undecided after the quick check. It is is therefore mostly useful to speed up computation for small polygons with complex shapes (say a country boundary on Earth), as the spherical cap will be small and hence will reliably identify a large part of the sphere as outside, whereas the full check can be more computing intensive. A typical use case is therefore: </p> <pre>  {@code // compute region, plus an enclosing spherical cap SphericalPolygonsSet complexShape = ...; EnclosingBall<Sphere2D, S2Point> cap = complexShape.getEnclosingCap(); // check lots of points}for (Cartesian3D p : points)  final Location l; if (cap.contains(p)) { // we cannot be sure where the point is // we need to perform the full computation l = complexShape.checkPoint(v); } else { // no need to do further computation, // we already know the point is outside l = Location.OUTSIDE; } // use l ... } }</pre> <p> In the special cases of empty or whole sphere polygons, special spherical caps are returned, with angular radius set to negative or positive infinity so the   {@link EnclosingBall#contains(org.apache.commons.math4.geometry.Point) ball.contains(point)}method return always false or true. </p> <p> This method is <em>not</em> guaranteed to return the smallest enclosing cap. </p>
 * @return a spherical cap enclosing the polygon
 */
public EnclosingBall<Sphere2D,S2Point> getEnclosingCap(){
  if (isEmpty()) {
    return new EnclosingBall<>(S2Point.PLUS_K,Double.NEGATIVE_INFINITY);
  }
  if (isFull()) {
    return new EnclosingBall<>(S2Point.PLUS_K,Double.POSITIVE_INFINITY);
  }
  final BSPTree<Sphere2D> root=getTree(false);
  if (isEmpty(root.getMinus()) && isFull(root.getPlus())) {
    final Circle circle=(Circle)root.getCut().getHyperplane();
    return new EnclosingBall<>(new S2Point(circle.getPole()).negate(),0.5 * FastMath.PI);
  }
  if (isFull(root.getMinus()) && isEmpty(root.getPlus())) {
    final Circle circle=(Circle)root.getCut().getHyperplane();
    return new EnclosingBall<>(new S2Point(circle.getPole()),0.5 * FastMath.PI);
  }
  final List<Cartesian3D> points=getInsidePoints();
  final List<Vertex> boundary=getBoundaryLoops();
  for (  final Vertex loopStart : boundary) {
    int count=0;
    for (Vertex v=loopStart; count == 0 || v != loopStart; v=v.getOutgoing().getEnd()) {
      ++count;
      points.add(v.getLocation().getVector());
    }
  }
  final SphereGenerator generator=new SphereGenerator();
  final WelzlEncloser<Euclidean3D,Cartesian3D> encloser=new WelzlEncloser<>(getTolerance(),generator);
  EnclosingBall<Euclidean3D,Cartesian3D> enclosing3D=encloser.enclose(points);
  final Cartesian3D[] support3D=enclosing3D.getSupport();
  final double r=enclosing3D.getRadius();
  final double h=enclosing3D.getCenter().getNorm();
  if (h < getTolerance()) {
    EnclosingBall<Sphere2D,S2Point> enclosingS2=new EnclosingBall<>(S2Point.PLUS_K,Double.POSITIVE_INFINITY);
    for (    Cartesian3D outsidePoint : getOutsidePoints()) {
      final S2Point outsideS2=new S2Point(outsidePoint);
      final BoundaryProjection<Sphere2D> projection=projectToBoundary(outsideS2);
      if (FastMath.PI - projection.getOffset() < enclosingS2.getRadius()) {
        enclosingS2=new EnclosingBall<>(outsideS2.negate(),FastMath.PI - projection.getOffset(),(S2Point)projection.getProjected());
      }
    }
    return enclosingS2;
  }
  final S2Point[] support=new S2Point[support3D.length];
  for (int i=0; i < support3D.length; ++i) {
    support[i]=new S2Point(support3D[i]);
  }
  final EnclosingBall<Sphere2D,S2Point> enclosingS2=new EnclosingBall<>(new S2Point(enclosing3D.getCenter()),FastMath.acos((1 + h * h - r * r) / (2 * h)),support);
  return enclosingS2;
}