private int addNeighbors(List<Acre> left, List<Acre> right) {
   assert left.size() == right.size();
   int count = 0;
   for (int i = 0, j = left.size(); i < j; i++) {
     Acre l = left.get(i);
     Acre r = right.get(i);
     if (l != null && r != null) {
       l.addNeighbor(r, indexOfMyNeighbor(l, r));
       r.addNeighbor(l, indexOfMyNeighbor(r, l));
       count++;
     }
   }
   return count;
 }
 private void assignVertexIds() {
   pointLocator = new PointLocator();
   for (Acre acre : sector.getInnerAcres()) {
     Point[] midpoints = AcreSeamSeq.buildMidpoints(acre);
     acre.seamStartVertexIds = buildSeamStarts(acre.packId, midpoints);
     acre.zoneStartVertexIds = buildZoneStarts(acre.packId, acre.points.length);
     acre.topographyDef = buildTopographyDef(acre.center, acre.points);
   }
   for (Acre acre : sector.getSharedAcres()) {
     Point[] midpoints = AcreSeamSeq.buildMidpoints(acre);
     acre.seamStartVertexIds = buildSeamStarts(acre.packId, midpoints);
     acre.zoneStartVertexIds = buildZoneStarts(acre.packId, acre.points.length);
     acre.topographyDef = buildTopographyDef(acre.center, acre.points);
   }
 }
  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);
          }
        }
      }
    }
  }
 private int addNeighbors(
     Acre[] acresA, int rowA, int lengthA, Acre[] acresB, int rowB, int lengthB) {
   if (lengthA <= 0 || lengthB <= 0) {
     return 0; // no-op
   }
   if (lengthB > lengthA) {
     int t = rowA;
     rowA = rowB;
     rowB = t;
     t = lengthA;
     lengthA = lengthB;
     lengthB = t;
   }
   if (lengthA == lengthB) {
     for (int i = 0; i < lengthA; i++) {
       Acre acreA = acresA[rowA + i];
       Acre acreB = acresB[rowB + i];
       acreA.addNeighbor(acreB, indexOfMyNeighbor(acreA, acreB));
       acreB.addNeighbor(acreA, indexOfMyNeighbor(acreB, acreA));
     }
     return lengthA * 2;
   } else {
     assert lengthA <= lengthB + 2;
     if (lengthA == lengthB + 1) {
       for (int i = 0; i < lengthB; i++) {
         Acre acreA = acresA[rowA + i];
         Acre acreB = acresB[rowB + i];
         Acre acreC = acresA[rowA + i + 1];
         acreA.addNeighbor(acreB, indexOfMyNeighbor(acreA, acreB));
         acreB.addNeighbor(acreA, indexOfMyNeighbor(acreB, acreA));
         acreB.addNeighbor(acreC, indexOfMyNeighbor(acreB, acreC));
         acreC.addNeighbor(acreB, indexOfMyNeighbor(acreC, acreB));
       }
       return lengthB * 4;
     } else {
       for (int i = 0; i < lengthB; i++) {
         Acre acreA = acresA[rowA + i + 1];
         Acre acreB = acresB[rowB + i];
         acreA.addNeighbor(acreB, indexOfMyNeighbor(acreA, acreB));
         acreB.addNeighbor(acreA, indexOfMyNeighbor(acreB, acreA));
       }
       return lengthB * 4;
     }
   }
 }
 private static void debugAcre(
     Acre a, String label, Set<Long> visitedAcreIds, Set<String> show, OutputGraph out) {
   if (visitedAcreIds != null) {
     visitedAcreIds.add(a.id);
   }
   double x = 0, y = 0;
   int l = a.points.length;
   double[] coords = new double[l * 2 + 2];
   for (int j = 0; j <= l; j++) {
     Point p = a.points[j % l].toPoint(1000.0);
     coords[j * 2] = p.x;
     coords[j * 2 + 1] = p.z;
     if (j < l) {
       x += p.x;
       y += p.z;
     }
   }
   x /= l;
   y /= l;
   for (int j = 0; j <= l; j++) {
     coords[j * 2] = coords[j * 2] * 0.85 + x * 0.15;
     coords[j * 2 + 1] = coords[j * 2 + 1] * 0.85 + y * 0.15;
   }
   if (label == null) {
     out.addLine(Color.white, coords);
   } else {
     Color polyColor, textColor = Color.black;
     AbstractCartographicElement parent = a.getParent();
     boolean inverted = parent instanceof Sector && ((Sector) parent).isInverted();
     int neighbors = 0;
     for (long id : a.neighbors) {
       if (id != 0) {
         neighbors++;
       }
     }
     switch (a.flavor) {
       case INNER1:
         polyColor = inverted ? Color.green : Color.red;
         break;
       case INNER2:
         polyColor = inverted ? Color.red : Color.green;
         break;
       case INNER3:
         //                    polyColor = Color.blue;
         //                    break;
       case DUAL_SECTOR:
         polyColor = new Color(51, 51, 255);
         break;
       case MULTI_SECTOR:
         polyColor = Color.gray.brighter();
         textColor = Color.black;
         break;
       default:
         polyColor = Color.black;
         textColor = Color.red;
         break;
     }
     if (neighbors < 6) {
       polyColor = polyColor.darker().darker();
       if (textColor == Color.black) {
         textColor = Color.white;
       }
       out.addPoly(polyColor, coords);
       double[] closed = new double[coords.length + 2];
       System.arraycopy(coords, 0, closed, 0, coords.length);
       System.arraycopy(coords, 0, closed, coords.length, 2);
       out.addLine(polyColor, closed);
     } else {
       out.addPoly(polyColor, coords);
     }
     if (show.contains("labels")) {
       if (label.contains("~")) {
         Matcher matcher = Pattern.compile("^(.*):\\[(.*)~(.*)]$").matcher(label);
         matcher.find();
         out.addLabel(
             textColor,
             matcher.group(1) + "\n" + matcher.group(2) + "\n" + matcher.group(3),
             x,
             y);
       } else {
         Matcher matcher = Pattern.compile("^(.*?):(.*)$").matcher(label);
         matcher.find();
         out.addLabel(textColor, matcher.group(1) + "\n" + matcher.group(2), x, y);
       }
     }
   }
 }