int _compareAngles(int index1, int index2) {
    int vert1 = m_bunchEdgeEndPoints.get(index1);
    Point2D pt1 = new Point2D();
    m_shape.getXY(vert1, pt1);
    Point2D pt2 = new Point2D();
    int vert2 = m_bunchEdgeEndPoints.get(index2);
    m_shape.getXY(vert2, pt2);

    if (pt1.isEqual(pt2)) return 0; // overlap case

    int vert10 = m_bunchEdgeCenterPoints.get(index1);
    Point2D pt10 = new Point2D();
    m_shape.getXY(vert10, pt10);

    int vert20 = m_bunchEdgeCenterPoints.get(index2);
    Point2D pt20 = new Point2D();
    m_shape.getXY(vert20, pt20);
    // _ASSERT(pt10.isEqual(pt20));

    Point2D v1 = new Point2D();
    v1.sub(pt1, pt10);
    Point2D v2 = new Point2D();
    v2.sub(pt2, pt20);
    int result = Point2D._compareVectors(v1, v2);
    return result;
  }
 private void _removeAngleSortInfo(int vertex) {
   int angleIndex = m_shape.getUserIndex(vertex, m_userIndexSortedAngleIndexToVertex);
   if (angleIndex != -1) {
     m_bunchEdgeIndices.set(angleIndex, -1);
     m_shape.setUserIndex(vertex, m_userIndexSortedAngleIndexToVertex, -1);
   }
 }
 private void _transferVertexData(int vertexFrom, int vertexTo) {
   int v1 = m_shape.getUserIndex(vertexTo, m_userIndexSortedIndexToVertex);
   int v2 = m_shape.getUserIndex(vertexTo, m_userIndexSortedAngleIndexToVertex);
   m_shape.transferAllDataToTheVertex(vertexFrom, vertexTo);
   m_shape.setUserIndex(vertexTo, m_userIndexSortedIndexToVertex, v1);
   m_shape.setUserIndex(vertexTo, m_userIndexSortedAngleIndexToVertex, v2);
 }
 int _compareVerticesSimple(int v1, int v2) {
   Point2D pt1 = new Point2D();
   m_shape.getXY(v1, pt1);
   Point2D pt2 = new Point2D();
   m_shape.getXY(v2, pt2);
   int res = pt1.compare(pt2);
   return res;
 }
  private void _beforeRemoveVertex(int vertex, boolean bChangePathFirst) {
    int vertexlistIndex = m_shape.getUserIndex(vertex, m_userIndexSortedIndexToVertex);
    // _ASSERT(m_sortedVertices.getData(vertexlistIndex) != 0xdeadbeef);
    if (m_nextVertexToProcess == vertexlistIndex) {
      m_nextVertexToProcess = m_sortedVertices.getNext(m_nextVertexToProcess);
    }

    if (m_firstCoincidentVertex == vertexlistIndex)
      m_firstCoincidentVertex = m_sortedVertices.getNext(m_firstCoincidentVertex);

    m_sortedVertices.deleteElement(m_sortedVerticesListIndex, vertexlistIndex);
    _removeAngleSortInfo(vertex);
    if (bChangePathFirst) {
      int path = m_shape.getPathFromVertex(vertex);
      if (path != -1) {
        int first = m_shape.getFirstVertex(path);
        if (first == vertex) {
          int next = m_shape.getNextVertex(vertex);
          if (next != vertex) m_shape.setFirstVertex_(path, next);
          else {
            m_shape.setFirstVertex_(path, -1);
            m_shape.setLastVertex_(path, -1);
          }
        }
      }
    }
  }
 private boolean _getDirection(int vert1, int vert2) {
   if (m_shape.getNextVertex(vert2) == vert1) {
     // _ASSERT(m_shape.getPrevVertex(vert1) == vert2);
     return false;
   } else {
     // _ASSERT(m_shape.getPrevVertex(vert2) == vert1);
     // _ASSERT(m_shape.getNextVertex(vert1) == vert2);
     return true;
   }
 }
  private boolean _removeSpike(int vertexIn) {
    // m_shape.dbgVerifyIntegrity(vertex);//debug
    int vertex = vertexIn;

    // _ASSERT(m_shape.isEqualXY(m_shape.getNextVertex(vertex),
    // m_shape.getPrevVertex(vertex)));
    boolean bFound = false;
    while (true) {
      int next = m_shape.getNextVertex(vertex);
      int prev = m_shape.getPrevVertex(vertex);
      if (next == vertex) { // last vertex in a ring
        _beforeRemoveVertex(vertex, true);
        m_shape.removeVertexInternal_(vertex, false);
        return true;
      }

      if (!m_shape.isEqualXY(next, prev)) break;

      bFound = true;
      _removeAngleSortInfo(prev);
      _removeAngleSortInfo(next);
      _beforeRemoveVertex(vertex, true);
      m_shape.removeVertexInternal_(vertex, false);
      // m_shape.dbgVerifyIntegrity(prev);//debug
      _transferVertexData(next, prev);
      _beforeRemoveVertex(next, true);
      m_shape.removeVertexInternal_(next, true);
      if (next == prev) break; // deleted the last vertex

      // m_shape.dbgVerifyIntegrity(prev);//debug

      vertex = prev;
    }
    return bFound;
  }
  private boolean _processBunch() {
    boolean bModified = false;
    int iter = 0;
    Point2D ptCenter = new Point2D();
    while (true) {
      m_dbgCounter++; // only for debugging
      iter++;
      // _ASSERT(iter < 10);
      if (m_bunchEdgeEndPoints == null) {
        m_bunchEdgeEndPoints = new AttributeStreamOfInt32(0);
        m_bunchEdgeCenterPoints = new AttributeStreamOfInt32(0);
        m_bunchEdgeIndices = new AttributeStreamOfInt32(0);
      } else {
        m_bunchEdgeEndPoints.clear(false);
        m_bunchEdgeCenterPoints.clear(false);
        m_bunchEdgeIndices.clear(false);
      }

      int currentVertex = m_firstCoincidentVertex;
      int index = 0;
      boolean bFirst = true;
      while (currentVertex != m_nextVertexToProcess) {
        int v = m_sortedVertices.getData(currentVertex);
        { // debug
          Point2D pt = new Point2D();
          m_shape.getXY(v, pt);
          double y = pt.x;
        }
        if (bFirst) {
          m_shape.getXY(v, ptCenter);
          bFirst = false;
        }
        int vertP = m_shape.getPrevVertex(v);
        int vertN = m_shape.getNextVertex(v);
        // _ASSERT(vertP != vertN || m_shape.getPrevVertex(vertN) == v
        // && m_shape.getNextVertex(vertP) == v);

        int id = m_shape.getUserIndex(vertP, m_userIndexSortedAngleIndexToVertex);
        if (id != 0xdeadbeef) // avoid adding a point twice
        {
          // _ASSERT(id == -1);
          m_bunchEdgeEndPoints.add(vertP);
          m_shape.setUserIndex(vertP, m_userIndexSortedAngleIndexToVertex, 0xdeadbeef); // mark
          // that
          // it
          // has
          // been
          // already
          // added
          m_bunchEdgeCenterPoints.add(v);
          m_bunchEdgeIndices.add(index++);
        }

        int id2 = m_shape.getUserIndex(vertN, m_userIndexSortedAngleIndexToVertex);
        if (id2 != 0xdeadbeef) // avoid adding a point twice
        {
          // _ASSERT(id2 == -1);
          m_bunchEdgeEndPoints.add(vertN);
          m_shape.setUserIndex(vertN, m_userIndexSortedAngleIndexToVertex, 0xdeadbeef); // mark
          // that
          // it
          // has
          // been
          // already
          // added
          m_bunchEdgeCenterPoints.add(v);
          m_bunchEdgeIndices.add(index++);
        }

        currentVertex = m_sortedVertices.getNext(currentVertex);
      }

      if (m_bunchEdgeEndPoints.size() < 2) break;

      // Sort the bunch edpoints by angle (angle between the axis x and
      // the edge, connecting the endpoint with the bunch center)
      m_bunchEdgeIndices.Sort(0, m_bunchEdgeIndices.size(), new SimplificatorAngleComparer(this));
      // SORTDYNAMICARRAYEX(m_bunchEdgeIndices, int, 0,
      // m_bunchEdgeIndices.size(), SimplificatorAngleComparer, this);

      for (int i = 0, n = m_bunchEdgeIndices.size(); i < n; i++) {
        int indexL = m_bunchEdgeIndices.get(i);
        int vertex = m_bunchEdgeEndPoints.get(indexL);
        m_shape.setUserIndex(vertex, m_userIndexSortedAngleIndexToVertex, i); // rember the
        // sort by angle
        // order
        { // debug
          Point2D pt = new Point2D();
          m_shape.getXY(vertex, pt);
          double y = pt.x;
        }
      }

      boolean bCrossOverResolved = _processCrossOvers(ptCenter); // see of
      // there
      // are
      // crossing
      // over
      // edges.
      for (int i = 0, n = m_bunchEdgeIndices.size(); i < n; i++) {
        int indexL = m_bunchEdgeIndices.get(i);
        if (indexL == -1) continue;
        int vertex = m_bunchEdgeEndPoints.get(indexL);
        m_shape.setUserIndex(vertex, m_userIndexSortedAngleIndexToVertex, -1); // remove
        // mapping
      }

      if (bCrossOverResolved) {
        bModified = true;
        continue;
      }

      break;
    }

    return bModified;
  }
  private void _fixOrphanVertices() {
    int pathCount = 0;
    // clean any path info
    for (int node = m_sortedVertices.getFirst(m_sortedVertices.getFirstList());
        node != -1;
        node = m_sortedVertices.getNext(node)) {
      int vertex = m_sortedVertices.getData(node);
      m_shape.setPathToVertex_(vertex, -1);
    }
    int geometrySize = 0;
    for (int path = m_shape.getFirstPath(m_geometry); path != -1; ) {
      int first = m_shape.getFirstVertex(path);
      if (first == -1 || m_shape.getPathFromVertex(first) != -1) {
        int p = path;
        path = m_shape.getNextPath(path);
        m_shape.removePathOnly_(p);
        continue;
      }

      m_shape.setPathToVertex_(first, path);
      int pathSize = 1;
      for (int vertex = m_shape.getNextVertex(first);
          vertex != first;
          vertex = m_shape.getNextVertex(vertex)) {
        // _ASSERT(m_shape.getPathFromVertex(vertex) == -1);
        m_shape.setPathToVertex_(vertex, path);
        // _ASSERT(m_shape.getNextVertex(m_shape.getPrevVertex(vertex))
        // == vertex);
        pathSize++;
      }
      m_shape.setPathSize_(path, pathSize);
      m_shape.setLastVertex_(path, m_shape.getPrevVertex(first));
      geometrySize += pathSize;
      pathCount++;
      path = m_shape.getNextPath(path);
    }

    // produce new paths for the orphan vertices.
    for (int node = m_sortedVertices.getFirst(m_sortedVertices.getFirstList());
        node != -1;
        node = m_sortedVertices.getNext(node)) {
      int vertex = m_sortedVertices.getData(node);
      if (m_shape.getPathFromVertex(vertex) != -1) continue;
      int path = m_shape.insertPath(m_geometry, -1);
      int pathSize = 0;
      int first = vertex;
      while (true) {
        m_shape.setPathToVertex_(vertex, path);
        pathSize++;
        int next = m_shape.getNextVertex(vertex);
        // _ASSERT(m_shape.getNextVertex(m_shape.getPrevVertex(vertex))
        // == vertex);
        if (next == first) break;
        vertex = next;
      }

      m_shape.setClosedPath(path, true);

      m_shape.setPathSize_(path, pathSize);
      m_shape.setFirstVertex_(path, first);
      m_shape.setLastVertex_(path, m_shape.getPrevVertex(first));
      geometrySize += pathSize;
      pathCount++;
    }
    m_shape.setGeometryPathCount_(m_geometry, pathCount);
    int totalPointCount = m_shape.getTotalPointCount() - m_shape.getPointCount(m_geometry);
    m_shape.setGeometryVertexCount_(m_geometry, geometrySize);
    m_shape.setTotalPointCount_(totalPointCount + geometrySize);
  }
  private boolean _cleanupSpikes() {
    boolean bModified = false;
    for (int path = m_shape.getFirstPath(m_geometry); path != -1; ) {
      int vertex = m_shape.getFirstVertex(path);
      for (int vindex = 0, n = m_shape.getPathSize(path); vindex < n && n > 1; ) {
        int prev = m_shape.getPrevVertex(vertex);
        int next = m_shape.getNextVertex(vertex);
        if (m_shape.isEqualXY(prev, next)) {
          bModified = true;
          _beforeRemoveVertex(vertex, false);
          m_shape.removeVertex(vertex, true); // not internal, because
          // path is valid at this
          // point
          _beforeRemoveVertex(next, false);
          m_shape.removeVertex(next, true);
          vertex = prev;
          vindex = 0;
          n = m_shape.getPathSize(path);
        } else {
          vertex = next;
          vindex++;
        }
      }

      if (m_shape.getPathSize(path) < 2) {
        int vertexL = m_shape.getFirstVertex(path);
        for (int vindex = 0, n = m_shape.getPathSize(path); vindex < n; vindex++) {
          _beforeRemoveVertex(vertexL, false);
          vertexL = m_shape.getNextVertex(vertexL);
        }

        path = m_shape.removePath(path);
        bModified = true;
      } else path = m_shape.getNextPath(path);
    }

    return bModified;
  }
  private void _resolveOverlapOddEven(
      boolean bDirection1,
      boolean bDirection2,
      int vertexA1,
      int vertexB1,
      int vertexA2,
      int vertexB2) {
    if (bDirection1 != bDirection2) {
      if (bDirection1) {
        // _ASSERT(m_shape.getNextVertex(vertexA1) == vertexB1);
        // _ASSERT(m_shape.getNextVertex(vertexB2) == vertexA2);
        m_shape.setNextVertex_(vertexA1, vertexA2); // B1< B2
        m_shape.setPrevVertex_(vertexA2, vertexA1); // | |
        m_shape.setNextVertex_(vertexB2, vertexB1); // | |
        m_shape.setPrevVertex_(vertexB1, vertexB2); // A1 >A2

        _transferVertexData(vertexA2, vertexA1);
        _beforeRemoveVertex(vertexA2, true);
        m_shape.removeVertexInternal_(vertexA2, true);
        _removeAngleSortInfo(vertexA1);
        _transferVertexData(vertexB2, vertexB1);
        _beforeRemoveVertex(vertexB2, false);
        m_shape.removeVertexInternal_(vertexB2, false);
        _removeAngleSortInfo(vertexB1);
      } else {
        // _ASSERT(m_shape.getNextVertex(vertexB1) == vertexA1);
        // _ASSERT(m_shape.getNextVertex(vertexA2) == vertexB2);
        m_shape.setNextVertex_(vertexA2, vertexA1); // B1 B2<
        m_shape.setPrevVertex_(vertexA1, vertexA2); // | |
        m_shape.setNextVertex_(vertexB1, vertexB2); // | |
        m_shape.setPrevVertex_(vertexB2, vertexB1); // A1< A2

        _transferVertexData(vertexA2, vertexA1);
        _beforeRemoveVertex(vertexA2, false);
        m_shape.removeVertexInternal_(vertexA2, false);
        _removeAngleSortInfo(vertexA1);
        _transferVertexData(vertexB2, vertexB1);
        _beforeRemoveVertex(vertexB2, true);
        m_shape.removeVertexInternal_(vertexB2, true);
        _removeAngleSortInfo(vertexB1);
      }
    } else // bDirection1 == bDirection2
    {
      if (!bDirection1) {
        // _ASSERT(m_shape.getNextVertex(vertexB1) == vertexA1);
        // _ASSERT(m_shape.getNextVertex(vertexB2) == vertexA2);
      } else {
        // _ASSERT(m_shape.getNextVertex(vertexA1) == vertexB1);
        // _ASSERT(m_shape.getNextVertex(vertexA2) == vertexB2);
      }

      // if (m_shape._RingParentageCheckInternal(vertexA1, vertexA2))
      {
        int a1 = bDirection1 ? vertexA1 : vertexB1;
        int a2 = bDirection2 ? vertexA2 : vertexB2;
        int b1 = bDirection1 ? vertexB1 : vertexA1;
        int b2 = bDirection2 ? vertexB2 : vertexA2;

        // m_shape.dbgVerifyIntegrity(a1);//debug
        // m_shape.dbgVerifyIntegrity(a2);//debug

        boolean bVisitedA1 = false;
        m_shape.setNextVertex_(a1, a2); // ^ | <--- ^ \ --.
        m_shape.setNextVertex_(a2, a1); // | | | ^ | |
        m_shape.setPrevVertex_(b1, b2); // | | | | | |
        m_shape.setPrevVertex_(b2, b1); // | | | | | |
        int v = b2; // | | | | =>| |
        while (v != a2) // | | . | | -. |
        { // | <-- | | | ./ | |
          int prev = m_shape.getPrevVertex(v); // | | | | | | | |
          int next = m_shape.getNextVertex(v); // <-+---<--- |
          // <-+---<--- |
          m_shape.setPrevVertex_(v, next); // --------. <--------
          m_shape.setNextVertex_(v, prev);
          bVisitedA1 |= v == a1;
          v = next;
        }

        if (!bVisitedA1) {
          // a case of two rings being merged
          int prev = m_shape.getPrevVertex(a2);
          int next = m_shape.getNextVertex(a2);
          m_shape.setPrevVertex_(a2, next);
          m_shape.setNextVertex_(a2, prev);
        } else {
          // merge happend on the same ring.
        }

        // m_shape.dbgVerifyIntegrity(b1);//debug
        // m_shape.dbgVerifyIntegrity(a1);//debug

        _transferVertexData(a2, a1);
        _beforeRemoveVertex(a2, true);
        m_shape.removeVertexInternal_(a2, false);
        _removeAngleSortInfo(a1);
        _transferVertexData(b2, b1);
        _beforeRemoveVertex(b2, true);
        m_shape.removeVertexInternal_(b2, false);
        _removeAngleSortInfo(b1);

        // m_shape.dbgVerifyIntegrity(b1);//debug
        // m_shape.dbgVerifyIntegrity(a1);//debug
      }
      // else
      // {
      // m_shape._ReverseRingInternal(vertexA2);
      // _ResolveOverlapOddEven(bDirection1, !bDirection2, vertexA1,
      // vertexB1, vertexA2, vertexB2);
      // }
    }
  }
  private boolean _detectAndResolveCrossOver(
      boolean bDirection1,
      boolean bDirection2,
      int vertexB1,
      int vertexA1,
      int vertexC1,
      int vertexB2,
      int vertexA2,
      int vertexC2) {
    // _ASSERT(!m_shape.isEqualXY(vertexB1, vertexB2));
    // _ASSERT(!m_shape.isEqualXY(vertexC1, vertexC2));

    if (vertexA1 == vertexA2) {
      _removeAngleSortInfo(vertexB1);
      _removeAngleSortInfo(vertexB2);
      return false;
    }

    // _ASSERT(!m_shape.isEqualXY(vertexB1, vertexC2));
    // _ASSERT(!m_shape.isEqualXY(vertexB1, vertexC1));
    // _ASSERT(!m_shape.isEqualXY(vertexB2, vertexC2));
    // _ASSERT(!m_shape.isEqualXY(vertexB2, vertexC1));
    // _ASSERT(!m_shape.isEqualXY(vertexA1, vertexB1));
    // _ASSERT(!m_shape.isEqualXY(vertexA1, vertexC1));
    // _ASSERT(!m_shape.isEqualXY(vertexA2, vertexB2));
    // _ASSERT(!m_shape.isEqualXY(vertexA2, vertexC2));

    // _ASSERT(m_shape.isEqualXY(vertexA1, vertexA2));

    // get indices of the vertices for the angle sort.
    int iB1 = m_shape.getUserIndex(vertexB1, m_userIndexSortedAngleIndexToVertex);
    int iC1 = m_shape.getUserIndex(vertexC1, m_userIndexSortedAngleIndexToVertex);
    int iB2 = m_shape.getUserIndex(vertexB2, m_userIndexSortedAngleIndexToVertex);
    int iC2 = m_shape.getUserIndex(vertexC2, m_userIndexSortedAngleIndexToVertex);
    // _ASSERT(iB1 >= 0);
    // _ASSERT(iC1 >= 0);
    // _ASSERT(iB2 >= 0);
    // _ASSERT(iC2 >= 0);
    // Sort the indices to restore the angle-sort order
    int[] ar = new int[8];
    int[] br = new int[4];

    ar[0] = 0;
    br[0] = iB1;
    ar[1] = 0;
    br[1] = iC1;
    ar[2] = 1;
    br[2] = iB2;
    ar[3] = 1;
    br[3] = iC2;
    for (int j = 1; j < 4; j++) // insertion sort
    {
      int key = br[j];
      int data = ar[j];
      int i = j - 1;
      while (i >= 0 && br[i] > key) {
        br[i + 1] = br[i];
        ar[i + 1] = ar[i];
        i--;
      }
      br[i + 1] = key;
      ar[i + 1] = data;
    }

    int detector = 0;
    if (ar[0] != 0) detector |= 1;
    if (ar[1] != 0) detector |= 2;
    if (ar[2] != 0) detector |= 4;
    if (ar[3] != 0) detector |= 8;
    if (detector != 5 && detector != 10) // not an overlap
    return false;

    if (bDirection1 == bDirection2) {
      if (bDirection1) {
        m_shape.setNextVertex_(vertexC2, vertexA1); // B1< >B2
        m_shape.setPrevVertex_(vertexA1, vertexC2); // \ /
        m_shape.setNextVertex_(vertexC1, vertexA2); // A1A2
        m_shape.setPrevVertex_(vertexA2, vertexC1); // / \ //
        // C2> <C1
      } else {
        m_shape.setPrevVertex_(vertexC2, vertexA1); // B1> <B2
        m_shape.setNextVertex_(vertexA1, vertexC2); // \ /
        m_shape.setPrevVertex_(vertexC1, vertexA2); // A1A2
        m_shape.setNextVertex_(vertexA2, vertexC1); // / \ //
        // C2< >C1
      }
    } else {
      if (bDirection1) {
        m_shape.setPrevVertex_(vertexA1, vertexB2); // B1< <B2
        m_shape.setNextVertex_(vertexB2, vertexA1); // \ /
        m_shape.setPrevVertex_(vertexA2, vertexC1); // A1A2
        m_shape.setNextVertex_(vertexC1, vertexA2); // / \ //
        // C2< <C1

      } else {
        m_shape.setNextVertex_(vertexA1, vertexB2); // B1> >B2
        m_shape.setPrevVertex_(vertexB2, vertexA1); // \ /
        m_shape.setNextVertex_(vertexA2, vertexC1); // A1A2
        m_shape.setPrevVertex_(vertexC1, vertexA2); // / \ //
        // C2> >C1

      }
    }

    return true;
  }
  private boolean _simplify() {
    boolean bChanged = false;
    boolean bNeedWindingRepeat = true;
    boolean bWinding = false;

    m_userIndexSortedIndexToVertex = -1;
    m_userIndexSortedAngleIndexToVertex = -1;

    int pointCount = m_shape.getPointCount(m_geometry);

    // Sort vertices lexicographically
    // Firstly copy allvertices to an array.
    AttributeStreamOfInt32 verticesSorter = new AttributeStreamOfInt32(0);
    verticesSorter.reserve(pointCount);

    for (int path = m_shape.getFirstPath(m_geometry);
        path != -1;
        path = m_shape.getNextPath(path)) {
      int vertex = m_shape.getFirstVertex(path);
      for (int index = 0, n = m_shape.getPathSize(path); index < n; index++) {
        verticesSorter.add(vertex);
        vertex = m_shape.getNextVertex(vertex);
      }
    }

    // Sort
    verticesSorter.Sort(0, pointCount, new SimplificatorVertexComparer(this));
    // SORTDYNAMICARRAYEX(verticesSorter, int, 0, pointCount,
    // SimplificatorVertexComparer, this);

    // Copy sorted vertices to the m_sortedVertices list. Make a mapping
    // from the edit shape vertices to the sorted vertices.
    m_userIndexSortedIndexToVertex = m_shape.createUserIndex(); // this index
    // is used
    // to map
    // from edit
    // shape
    // vertex to
    // the
    // m_sortedVertices
    // list
    m_sortedVertices = new IndexMultiDCList();
    m_sortedVerticesListIndex = m_sortedVertices.createList(0);
    for (int i = 0; i < pointCount; i++) {
      int vertex = verticesSorter.get(i);
      { // debug
        Point2D pt = new Point2D();
        m_shape.getXY(vertex, pt); // for debugging
        double y = pt.x;
      }
      int vertexlistIndex = m_sortedVertices.addElement(m_sortedVerticesListIndex, vertex);
      m_shape.setUserIndex(
          vertex,
          m_userIndexSortedIndexToVertex,
          vertexlistIndex); // remember the sorted list element on the
      // vertex.
      // When we remove a vertex, we also remove associated sorted list
      // element.
    }

    m_userIndexSortedAngleIndexToVertex = m_shape.createUserIndex(); // create
    // additional
    // list
    // to
    // store
    // angular
    // sort
    // mapping.

    m_nextVertexToProcess = -1;

    if (_cleanupSpikes()) // cleanup any spikes on the polygon.
    bChanged = true;

    // External iteration loop for the simplificator.
    // ST. I am not sure if it actually needs this loop. TODO: figure this
    // out.
    while (bNeedWindingRepeat) {
      bNeedWindingRepeat = false;

      int max_iter =
          m_shape.getPointCount(m_geometry) + 10 > 30
              ? 1000
              : (m_shape.getPointCount(m_geometry) + 10) * (m_shape.getPointCount(m_geometry) + 10);

      // Simplify polygon
      int iRepeatNum = 0;
      boolean bNeedRepeat = false;

      // Internal iteration loop for the simplificator.
      // ST. I am not sure if it actually needs this loop. TODO: figure
      // this out.
      do // while (bNeedRepeat);
      {
        bNeedRepeat = false;

        boolean bVertexRecheck = false;
        m_firstCoincidentVertex = -1;
        int coincidentCount = 0;
        Point2D ptFirst = new Point2D();
        Point2D pt = new Point2D();
        // Main loop of the simplificator. Go through the vertices and
        // for those that have same coordinates,
        for (int vlistindex = m_sortedVertices.getFirst(m_sortedVerticesListIndex);
            vlistindex != IndexMultiDCList.nullNode(); ) {
          int vertex = m_sortedVertices.getData(vlistindex);
          { // debug
            // Point2D pt = new Point2D();
            m_shape.getXY(vertex, pt);
            double d = pt.x;
          }

          if (m_firstCoincidentVertex != -1) {
            // Point2D pt = new Point2D();
            m_shape.getXY(vertex, pt);
            if (ptFirst.isEqual(pt)) {
              coincidentCount++;
            } else {
              ptFirst.setCoords(pt);
              m_nextVertexToProcess = vlistindex; // we remeber the
              // next index in
              // the member
              // variable to
              // allow it to
              // be updated if
              // a vertex is
              // removed
              // inside of the
              // _ProcessBunch.
              if (coincidentCount > 0) {
                boolean result = _processBunch(); // process a
                // bunch of
                // coinciding
                // vertices
                if (result) { // something has changed.
                  // Note that ProcessBunch may
                  // change m_nextVertexToProcess
                  // and m_firstCoincidentVertex.
                  bNeedRepeat = true;
                  if (m_nextVertexToProcess != IndexMultiDCList.nullNode()) {
                    int v = m_sortedVertices.getData(m_nextVertexToProcess);
                    m_shape.getXY(v, ptFirst);
                  }
                }
              }

              vlistindex = m_nextVertexToProcess;
              m_firstCoincidentVertex = vlistindex;
              coincidentCount = 0;
            }
          } else {
            m_firstCoincidentVertex = vlistindex;
            m_shape.getXY(m_sortedVertices.getData(vlistindex), ptFirst);
            coincidentCount = 0;
          }

          vlistindex = m_sortedVertices.getNext(vlistindex);
        }

        m_nextVertexToProcess = -1;

        if (coincidentCount > 0) {
          boolean result = _processBunch();
          if (result) bNeedRepeat = true;
        }

        if (iRepeatNum++ > 10) {
          throw new GeometryException("internal error.");
        }

        if (bNeedRepeat) _fixOrphanVertices(); // fix broken structure of the shape

        if (_cleanupSpikes()) bNeedRepeat = true;

        bNeedWindingRepeat |= bNeedRepeat && bWinding;

        bChanged |= bNeedRepeat;

      } while (bNeedRepeat);
    } // while (bNeedWindingRepeat)

    // Now process rings. Fix ring orientation and determine rings that need
    // to be deleted.

    m_shape.removeUserIndex(m_userIndexSortedIndexToVertex);
    m_shape.removeUserIndex(m_userIndexSortedAngleIndexToVertex);

    bChanged |= RingOrientationFixer.execute(m_shape, m_geometry, m_sortedVertices);

    return bChanged;
  }
  private boolean _processCrossOvers(Point2D ptCenter) {
    boolean bFound = false;

    // Resolve all overlaps
    boolean bContinue = true;
    while (bContinue) {
      // The nearest pairts in the middle of the list
      bContinue = false;
      int index1 = 0;
      if (m_bunchEdgeIndices.get(index1) == -1) index1 = _getNextEdgeIndex(index1);

      int index2 = _getNextEdgeIndex(index1);

      for (int i = 0, n = m_bunchEdgeIndices.size();
          i < n && index1 != -1 && index2 != -1 && index1 != index2;
          i++) {
        int edgeindex1 = m_bunchEdgeIndices.get(index1);
        int edgeindex2 = m_bunchEdgeIndices.get(index2);

        int vertexB1 = m_bunchEdgeEndPoints.get(edgeindex1);
        int vertexB2 = m_bunchEdgeEndPoints.get(edgeindex2);
        // _ASSERT(vertexB2 != vertexB1);

        int vertexA1 = m_shape.getNextVertex(vertexB1);
        if (!m_shape.isEqualXY(vertexA1, ptCenter)) vertexA1 = m_shape.getPrevVertex(vertexB1);
        int vertexA2 = m_shape.getNextVertex(vertexB2);
        if (!m_shape.isEqualXY(vertexA2, ptCenter)) vertexA2 = m_shape.getPrevVertex(vertexB2);

        // _ASSERT(m_shape.isEqualXY(vertexA1, vertexA2));
        // _ASSERT(m_shape.isEqualXY(vertexA1, ptCenter));

        boolean bDirection1 = _getDirection(vertexA1, vertexB1);
        boolean bDirection2 = _getDirection(vertexA2, vertexB2);
        int vertexC1 =
            bDirection1 ? m_shape.getPrevVertex(vertexA1) : m_shape.getNextVertex(vertexA1);
        int vertexC2 =
            bDirection2 ? m_shape.getPrevVertex(vertexA2) : m_shape.getNextVertex(vertexA2);

        boolean bOverlap = false;
        if (_removeSpike(vertexA1)) bOverlap = true;
        else if (_removeSpike(vertexA2)) bOverlap = true;
        else if (_removeSpike(vertexB1)) bOverlap = true;
        else if (_removeSpike(vertexB2)) bOverlap = true;
        else if (_removeSpike(vertexC1)) bOverlap = true;
        else if (_removeSpike(vertexC2)) bOverlap = true;

        if (!bOverlap && m_shape.isEqualXY(vertexB1, vertexB2)) {
          bOverlap = true;
          _resolveOverlap(bDirection1, bDirection2, vertexA1, vertexB1, vertexA2, vertexB2);
        }

        if (!bOverlap && m_shape.isEqualXY(vertexC1, vertexC2)) {
          bOverlap = true;
          _resolveOverlap(!bDirection1, !bDirection2, vertexA1, vertexC1, vertexA2, vertexC2);
        }

        if (bOverlap) bFound = true;

        bContinue |= bOverlap;

        index1 = _getNextEdgeIndex(index1);
        index2 = _getNextEdgeIndex(index1);
      }
    }

    if (!bFound) { // resolve all cross overs
      int index1 = 0;
      if (m_bunchEdgeIndices.get(index1) == -1) index1 = _getNextEdgeIndex(index1);

      int index2 = _getNextEdgeIndex(index1);

      for (int i = 0, n = m_bunchEdgeIndices.size();
          i < n && index1 != -1 && index2 != -1 && index1 != index2;
          i++) {
        int edgeindex1 = m_bunchEdgeIndices.get(index1);
        int edgeindex2 = m_bunchEdgeIndices.get(index2);

        int vertexB1 = m_bunchEdgeEndPoints.get(edgeindex1);
        int vertexB2 = m_bunchEdgeEndPoints.get(edgeindex2);

        int vertexA1 = m_shape.getNextVertex(vertexB1);
        if (!m_shape.isEqualXY(vertexA1, ptCenter)) vertexA1 = m_shape.getPrevVertex(vertexB1);
        int vertexA2 = m_shape.getNextVertex(vertexB2);
        if (!m_shape.isEqualXY(vertexA2, ptCenter)) vertexA2 = m_shape.getPrevVertex(vertexB2);

        // _ASSERT(m_shape.isEqualXY(vertexA1, vertexA2));
        // _ASSERT(m_shape.isEqualXY(vertexA1, ptCenter));

        boolean bDirection1 = _getDirection(vertexA1, vertexB1);
        boolean bDirection2 = _getDirection(vertexA2, vertexB2);
        int vertexC1 =
            bDirection1 ? m_shape.getPrevVertex(vertexA1) : m_shape.getNextVertex(vertexA1);
        int vertexC2 =
            bDirection2 ? m_shape.getPrevVertex(vertexA2) : m_shape.getNextVertex(vertexA2);

        if (_detectAndResolveCrossOver(
            bDirection1, bDirection2, vertexB1, vertexA1, vertexC1, vertexB2, vertexA2, vertexC2)) {
          bFound = true;
        }

        index1 = _getNextEdgeIndex(index1);
        index2 = _getNextEdgeIndex(index1);
      }
    }

    return bFound;
  }