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);
 }
 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 _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 _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 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;
  }