private void drawPolygonOutline(
     DrawContext dc,
     List<LatLon> locations,
     List<Boolean> edgeFlags,
     double[] altitudes,
     boolean[] terrainConformant,
     boolean enableCaps,
     int subdivisions,
     Vec4 referenceCenter) {
   PolygonGeometry geom =
       this.getPolygonGeometry(
           dc,
           locations,
           edgeFlags,
           altitudes,
           terrainConformant,
           enableCaps,
           subdivisions,
           referenceCenter);
   if (geom != null)
     this.getRenderer().drawGeometry(dc, geom.getOutlineIndexGeometry(), geom.getVertexGeometry());
 }
  private void makePolygon(
      DrawContext dc,
      List<LatLon> locations,
      List<Boolean> edgeFlags,
      double[] altitudes,
      boolean[] terrainConformant,
      boolean enableCaps,
      int subdivisions,
      Vec4 referenceCenter,
      PolygonGeometry dest) {
    if (locations.size() == 0) return;

    GeometryBuilder gb = this.getGeometryBuilder();

    Vec4[] polyPoints = new Vec4[locations.size() + 1];
    Boolean[] polyEdgeFlags = new Boolean[locations.size() + 1];
    Matrix[] polyTransform = new Matrix[1];
    int polyCount =
        this.computeCartesianPolygon(
            dc.getGlobe(), locations, edgeFlags, polyPoints, polyEdgeFlags, polyTransform);

    // Compute the winding order of the planar cartesian points. If the order is not
    // counter-clockwise, then
    // reverse the locations and points ordering.
    int winding = gb.computePolygonWindingOrder2(0, polyCount, polyPoints);
    if (winding != GeometryBuilder.COUNTER_CLOCKWISE) {
      gb.reversePoints(0, polyCount, polyPoints);
      gb.reversePoints(0, polyCount, polyEdgeFlags);
    }

    float[] polyVertices = new float[3 * polyCount];
    this.makePolygonVertices(polyCount, polyPoints, polyVertices);

    int fillDrawMode = GL.GL_TRIANGLES;
    int outlineDrawMode = GL.GL_LINES;

    int fillIndexCount = 0;
    int outlineIndexCount = 0;
    int vertexCount = 0;

    GeometryBuilder.IndexedTriangleArray ita = null;

    fillIndexCount += this.getEdgeFillIndexCount(polyCount, subdivisions);
    outlineIndexCount += this.getEdgeOutlineIndexCount(polyCount, subdivisions, polyEdgeFlags);
    vertexCount += this.getEdgeVertexCount(polyCount, subdivisions);

    if (enableCaps) {
      ita = gb.tessellatePolygon2(0, polyCount, polyVertices);
      for (int i = 0; i < subdivisions; i++) {
        gb.subdivideIndexedTriangleArray(ita);
      }

      fillIndexCount += ita.getIndexCount();
      vertexCount += ita.getVertexCount();
      // Bottom cap isn't drawn if airspace is collapsed.
      if (!this.isAirspaceCollapsed()) {
        fillIndexCount += ita.getIndexCount();
        vertexCount += ita.getVertexCount();
      }
    }

    int[] fillIndices = new int[fillIndexCount];
    int[] outlineIndices = new int[outlineIndexCount];
    float[] vertices = new float[3 * vertexCount];
    float[] normals = new float[3 * vertexCount];

    int fillIndexPos = 0;
    int outlineIndexPos = 0;
    int vertexPos = 0;

    this.makeEdge(
        dc,
        polyCount,
        polyVertices,
        polyEdgeFlags,
        altitudes,
        terrainConformant,
        subdivisions,
        GeometryBuilder.OUTSIDE,
        polyTransform[0],
        referenceCenter,
        fillIndexPos,
        fillIndices,
        outlineIndexPos,
        outlineIndices,
        vertexPos,
        vertices,
        normals);
    fillIndexPos += this.getEdgeFillIndexCount(polyCount, subdivisions);
    outlineIndexPos += this.getEdgeOutlineIndexCount(polyCount, subdivisions, polyEdgeFlags);
    vertexPos += this.getEdgeVertexCount(polyCount, subdivisions);

    if (enableCaps) {
      this.makeCap(
          dc,
          ita,
          altitudes[1],
          terrainConformant[1],
          GeometryBuilder.OUTSIDE,
          polyTransform[0],
          referenceCenter,
          fillIndexPos,
          fillIndices,
          vertexPos,
          vertices,
          normals);
      fillIndexPos += ita.getIndexCount();
      vertexPos += ita.getVertexCount();
      // Bottom cap isn't drawn if airspace is collapsed.
      if (!this.isAirspaceCollapsed()) {
        this.makeCap(
            dc,
            ita,
            altitudes[0],
            terrainConformant[0],
            GeometryBuilder.INSIDE,
            polyTransform[0],
            referenceCenter,
            fillIndexPos,
            fillIndices,
            vertexPos,
            vertices,
            normals);
        fillIndexPos += ita.getIndexCount();
        vertexPos += ita.getVertexCount();
      }
    }

    dest.getFillIndexGeometry().setElementData(fillDrawMode, fillIndexCount, fillIndices);
    dest.getOutlineIndexGeometry()
        .setElementData(outlineDrawMode, outlineIndexCount, outlineIndices);
    dest.getVertexGeometry().setVertexData(vertexCount, vertices);
    dest.getVertexGeometry().setNormalData(vertexCount, normals);
  }
  private PolygonGeometry getPolygonGeometry(
      DrawContext dc,
      List<LatLon> locations,
      List<Boolean> edgeFlags,
      double[] altitudes,
      boolean[] terrainConformant,
      boolean enableCaps,
      int subdivisions,
      Vec4 referenceCenter) {
    Object cacheKey =
        new Geometry.CacheKey(
            dc.getGlobe(),
            this.getClass(),
            "Polygon",
            locations,
            edgeFlags,
            altitudes[0],
            altitudes[1],
            terrainConformant[0],
            terrainConformant[1],
            enableCaps,
            subdivisions,
            referenceCenter);

    // Wrap geometry creation in a try/catch block. We do this to catch and handle OutOfMemoryErrors
    // caused during
    // tessellation of the polygon vertices. If the polygon cannot be tessellated, we replace the
    // polygon's
    // locations with an empty list to prevent subsequent tessellation attempts, and to avoid
    // rendering a misleading
    // representation by omitting any part of the geometry.
    try {
      PolygonGeometry geom = (PolygonGeometry) this.getGeometryCache().getObject(cacheKey);
      if (geom == null || this.isExpired(dc, geom.getVertexGeometry())) {
        if (geom == null) geom = new PolygonGeometry();
        this.makePolygon(
            dc,
            locations,
            edgeFlags,
            altitudes,
            terrainConformant,
            enableCaps,
            subdivisions,
            referenceCenter,
            geom);
        this.updateExpiryCriteria(dc, geom.getVertexGeometry());
        this.getGeometryCache().add(cacheKey, geom);
      }

      return geom;
    } catch (OutOfMemoryError e) {
      String message = Logging.getMessage("generic.ExceptionWhileTessellating", this);
      Logging.logger().log(java.util.logging.Level.SEVERE, message, e);

      //noinspection ThrowableInstanceNeverThrown
      dc.addRenderingException(new WWRuntimeException(message, e));

      this.handleUnsuccessfulGeometryCreation();
      return null;
    }
  }