private boolean isCCW(GeoPoint a, GeoPoint b, GeoPoint c) {
   Point i = a.toPoint(1.0);
   Point j = b.toPoint(1.0);
   Point k = c.toPoint(1.0);
   Vector cross = Vector.cross(i, j, k).normalize();
   Point l = i.translate(cross);
   double d = Vector.computeDistance(l.x, l.y, l.z);
   return d < 1.0;
 }
 private int indexOfMyNeighbor(Acre me, Acre neighbor) {
   for (int i = 0, j = 1, l = me.points.length; i < l; i++, j = (i + 1) % l) {
     GeoPoint pointA = me.points[i];
     GeoPoint pointB = me.points[j];
     for (GeoPoint point : neighbor.points) {
       if (point.equals(pointA)) {
         pointA = null;
         if (pointB == null) return i;
       } else if (point.equals(pointB)) {
         pointB = null;
         if (pointA == null) return i;
       }
     }
   }
   throw new AssertionError(
       "Failed to find the index of a neighbor: " + neighbor + " --> add to --> " + me);
 }
 public long findBetween(GeoPoint pointA, GeoPoint pointB) {
   Point a = pointA.toPoint(1000.0);
   Point b = pointB.toPoint(1000.0);
   double x = (a.x + b.x) * 0.5;
   double y = (a.y + b.y) * 0.5;
   double z = (a.z + b.z) * 0.5;
   double l = 1000.0 / Vector.computeDistance(x, y, z);
   x *= l;
   y *= l;
   z *= l;
   assert Math.abs(
           Vector.computeDistance(a.x - x, a.y - y, a.z - z)
               - Vector.computeDistance(b.x - x, b.y - y, b.z - z))
       < 0.0005;
   globalPointMap.intersect(bounds.load(x, y, z), this.reset());
   assert found;
   assert globalPointEntry.getSeq() >= 0;
   return globalPointEntry.getSeq();
 }
  public static void main(String[] args) {
    final Set<String> show =
        new HashSet<String>(Arrays.asList("point$ triangles acres neighbors sectors".split(" ")));
    //        final Set<String> show = new HashSet<String>(Arrays.asList("global-sectors
    // sectors".split(" ")));
    final Map<Long, Acre> acresByID = new HashMap<Long, Acre>();
    Consumer<Acre> acreConsumer =
        new Consumer<Acre>() {
          public void consume(Acre acre) {
            assert !acresByID.containsKey(acre.id);
            acresByID.put(acre.id, acre);
          }
        };

    Point[] points = {
      new Point(1.0, -1000.0, Math.sqrt(3) * -0.5),
      new Point(-1.0, -1000.0, Math.sqrt(3) * -0.5),
      new Point(0.0, -1000.0, Math.sqrt(3) * 0.5)
    };
    GeoPoint[] geoPoints = {
      GeoPoint.fromPoint(points[0]), GeoPoint.fromPoint(points[1]), GeoPoint.fromPoint(points[2])
    };
    for (int i = 0; i < 3; i++) {
      points[i] = geoPoints[i].toPoint(1000.0);
    }

    final OutputGraph out = new OutputGraph("Acres", new Dimension(2048, 2048), 0.0, 0.0, 1000.0);
    out.onClose(
        new Runnable() {
          public void run() {
            System.exit(0);
          }
        });

    Sector.MAP_ACRES_BY_CENTER_POINT.set(true);
    final GlobalSector gs = new GlobalSector(0, Globe.INSTANCE, geoPoints);
    final AtomicReference<Sector[]> sectors = new AtomicReference<Sector[]>();
    gs.setInit(
        gs.new Initializer(5, points[0], points[1], points[2], false, null) {

          @Override
          protected Sector[] getChildren(int length) {
            sectors.set(super.getChildren(length));
            return sectors.get();
          }

          @Override
          protected Sector buildChild(
              int index,
              GeoPoint a,
              GeoPoint b,
              GeoPoint c,
              Point i,
              Point j,
              Point k,
              boolean inverted) {
            TestSector sector = new TestSector(index, inverted, gs, new GeoPoint[] {a, b, c});
            sector.setInit(sector.new TestInitializer(new Point[] {i, j, k}, show, out));
            sectors.get()[index] = sector;
            return sector;
          }
        });
    gs.getInit().run();
    for (Sector s : sectors.get()) {
      s.getInit().run();
      s.edgesBuilt = new AtomicInteger();
    }
    if (show.contains("global-sectors")) {
      Point a = gs.points[0].toPoint(1000.0);
      Point b = gs.points[1].toPoint(1000.0);
      Point c = gs.points[2].toPoint(1000.0);
      double x = (a.x + b.x + c.x) / 3.0;
      double y = (a.z + b.z + c.z) / 3.0;
      if (show.contains("labels")) {
        out.addLabel(Color.orange, gs.getIDString(), x, y + 0.075);
      }
      x *= -0.02;
      y *= -0.02;
      out.addLine(
          Color.orange,
          a.x * 1.02 + x,
          a.z * 1.02 + y,
          b.x * 1.02 + x,
          b.z * 1.02 + y,
          c.x * 1.02 + x,
          c.z * 1.02 + y,
          a.x * 1.02 + x,
          a.z * 1.02 + y);
    }
    if (show.contains("sectors")) {
      for (Sector s : sectors.get()) {
        Point a = s.points[0].toPoint(1000.0);
        Point b = s.points[1].toPoint(1000.0);
        Point c = s.points[2].toPoint(1000.0);
        out.addLine(Color.magenta, a.x, a.z, b.x, b.z, c.x, c.z, a.x, a.z);
        if (show.contains("labels")) {
          double x = (a.x + b.x + c.x) / 3.0;
          double y = (a.z + b.z + c.z) / 3.0;
          out.addLabel(Color.magenta, s.getIDString(), x, y);
        }
      }
    }
    GeoFactory.combinePoints(Arrays.<GeoPointBasedElement>asList(sectors.get()), Globe.INSTANCE);
    Map<GeoPoint, Sector[]> sectorMap = new HashMap<GeoPoint, Sector[]>();
    for (Sector s : sectors.get()) {
      GeoFactory.addSectorByPoint(sectorMap, s, s.points[0]);
      GeoFactory.addSectorByPoint(sectorMap, s, s.points[1]);
      GeoFactory.addSectorByPoint(sectorMap, s, s.points[2]);
    }
    Collection<AcreBuilder> builders = new ArrayList<AcreBuilder>();
    for (final Sector s : sectors.get()) {
      builders.add(
          new AcreBuilder(
              GeoSpec.SECTOR_DIVISIONS.iGet(), false, s, sectorMap, null, acreConsumer) {

            @Override
            protected int[] findNearCornerAcres() {
              int[] nearCornerAcres = super.findNearCornerAcres();
              if (show.contains("near-edge-acres")) {
                System.out.println("Corner A: " + nearCornerAcres[0]);
                System.out.println("Corner B: " + nearCornerAcres[1]);
                System.out.println("Corner C: " + nearCornerAcres[2]);
              }
              return nearCornerAcres;
            }

            @Override
            protected Map<Edge, int[]> findNearEdgeAcres() {
              Map<Edge, int[]> nearEdgeAcres = super.findNearEdgeAcres();
              if (show.contains("near-edge-acres")) {
                System.out.println("Edge AB: " + Arrays.toString(nearEdgeAcres.get(Edge.AB)));
                System.out.println("Edge BC: " + Arrays.toString(nearEdgeAcres.get(Edge.BC)));
                System.out.println("Edge CA: " + Arrays.toString(nearEdgeAcres.get(Edge.CA)));
              }
              return nearEdgeAcres;
            }
          });
    }
    for (AcreBuilder builder : builders) {
      builder.run(0);
    }
    for (AcreBuilder builder : builders) {
      builder.secondRun().run();
    }
    for (Acre a : acresByID.values()) {
      int c = 0;
      Point p = a.center.toPoint(1000.0);
      for (Sector s : sectors.get()) {
        Acre acre = s.acres.get(p);
        if (acre != null) {
          assert acre == a;
          c++;
        }
      }
      assert a.flavor == Acre.Flavor.MULTI_SECTOR
          ? c >= 5
          : (a.flavor == Acre.Flavor.DUAL_SECTOR ? c == 2 : c == 1);
    }

    if (show.contains("acres")) {
      Set<Long> visitedAcreIds = new HashSet<Long>();
      for (Sector s : sectors.get()) {
        Acre[] acres = s.getInnerAcres();
        for (Acre acre : acres) {
          debugAcre(acre, acre.getIDString(), visitedAcreIds, show, out);
        }
        acres = s.getSharedAcres();
        for (Acre acre : acres) {
          if (acre != null) {
            debugAcre(acre, acre.getIDString(), visitedAcreIds, show, out);
          }
        }
      }
      assert visitedAcreIds.containsAll(acresByID.keySet());
    }
    if (show.contains("neighbors")) {
      double v = 0.7, w = 1.0 - v;
      for (Acre a : acresByID.values()) {
        for (long neighborID : a.neighbors) {
          Acre n = acresByID.get(neighborID);
          if (n != null) {
            Point s = a.center.toPoint(1000.0);
            Point t = n.center.toPoint(1000.0);
            out.addArrow(
                Color.white,
                s.x * v + t.x * w,
                s.z * v + t.z * w,
                s.x * w + t.x * v,
                s.z * w + t.z * v);
          }
        }
      }
    }
  }