private static void addEdges(Vector edges, Vector curves, int curvetag) {
   Enumeration enum_ = curves.elements();
   while (enum_.hasMoreElements()) {
     Curve c = (Curve) enum_.nextElement();
     if (c.getOrder() > 0) {
       edges.add(new Edge(c, curvetag));
     }
   }
 }
 public static void finalizeSubCurves(Vector subcurves, Vector chains) {
   int numchains = chains.size();
   if (numchains == 0) {
     return;
   }
   if ((numchains & 1) != 0) {
     throw new RuntimeException("Odd number of chains!");
   }
   ChainEnd[] endlist = new ChainEnd[numchains];
   chains.toArray(endlist);
   for (int i = 1; i < numchains; i += 2) {
     ChainEnd open = endlist[i - 1];
     ChainEnd close = endlist[i];
     CurveLink subcurve = open.linkTo(close);
     if (subcurve != null) {
       subcurves.add(subcurve);
     }
   }
   chains.clear();
 }
  public static void resolveLinks(Vector subcurves, Vector chains, Vector links) {
    int numlinks = links.size();
    CurveLink[] linklist;
    if (numlinks == 0) {
      linklist = EmptyLinkList;
    } else {
      if ((numlinks & 1) != 0) {
        throw new RuntimeException("Odd number of new curves!");
      }
      linklist = new CurveLink[numlinks + 2];
      links.toArray(linklist);
    }
    int numchains = chains.size();
    ChainEnd[] endlist;
    if (numchains == 0) {
      endlist = EmptyChainList;
    } else {
      if ((numchains & 1) != 0) {
        throw new RuntimeException("Odd number of chains!");
      }
      endlist = new ChainEnd[numchains + 2];
      chains.toArray(endlist);
    }
    int curchain = 0;
    int curlink = 0;
    chains.clear();
    ChainEnd chain = endlist[0];
    ChainEnd nextchain = endlist[1];
    CurveLink link = linklist[0];
    CurveLink nextlink = linklist[1];
    while (chain != null || link != null) {
      /*
       * Strategy 1:
       * Connect chains or links if they are the only things left...
       */
      boolean connectchains = (link == null);
      boolean connectlinks = (chain == null);

      if (!connectchains && !connectlinks) {
        // assert(link != null && chain != null);
        /*
         * Strategy 2:
         * Connect chains or links if they close off an open area...
         */
        connectchains = ((curchain & 1) == 0 && chain.getX() == nextchain.getX());
        connectlinks = ((curlink & 1) == 0 && link.getX() == nextlink.getX());

        if (!connectchains && !connectlinks) {
          /*
           * Strategy 3:
           * Connect chains or links if their successor is
           * between them and their potential connectee...
           */
          double cx = chain.getX();
          double lx = link.getX();
          connectchains =
              (nextchain != null && cx < lx && obstructs(nextchain.getX(), lx, curchain));
          connectlinks = (nextlink != null && lx < cx && obstructs(nextlink.getX(), cx, curlink));
        }
      }
      if (connectchains) {
        CurveLink subcurve = chain.linkTo(nextchain);
        if (subcurve != null) {
          subcurves.add(subcurve);
        }
        curchain += 2;
        chain = endlist[curchain];
        nextchain = endlist[curchain + 1];
      }
      if (connectlinks) {
        ChainEnd openend = new ChainEnd(link, null);
        ChainEnd closeend = new ChainEnd(nextlink, openend);
        openend.setOtherEnd(closeend);
        chains.add(openend);
        chains.add(closeend);
        curlink += 2;
        link = linklist[curlink];
        nextlink = linklist[curlink + 1];
      }
      if (!connectchains && !connectlinks) {
        // assert(link != null);
        // assert(chain != null);
        // assert(chain.getEtag() == link.getEtag());
        chain.addLink(link);
        chains.add(chain);
        curchain++;
        chain = nextchain;
        nextchain = endlist[curchain + 1];
        curlink++;
        link = nextlink;
        nextlink = linklist[curlink + 1];
      }
    }
  }
 private Vector pruneEdges(Vector edges) {
   int numedges = edges.size();
   if (numedges < 2) {
     return edges;
   }
   Edge[] edgelist = (Edge[]) edges.toArray(new Edge[numedges]);
   Arrays.sort(edgelist, YXTopComparator);
   Edge e;
   int left = 0;
   int right = 0;
   int cur = 0;
   int next = 0;
   double yrange[] = new double[2];
   Vector subcurves = new Vector();
   Vector chains = new Vector();
   Vector links = new Vector();
   // Active edges are between left (inclusive) and right (exclusive)
   while (left < numedges) {
     double y = yrange[0];
     // Prune active edges that fall off the top of the active y range
     for (cur = next = right - 1; cur >= left; cur--) {
       e = edgelist[cur];
       if (e.getCurve().getYBot() > y) {
         if (next > cur) {
           edgelist[next] = e;
         }
         next--;
       }
     }
     left = next + 1;
     // Grab a new "top of Y range" if the active edges are empty
     if (left >= right) {
       if (right >= numedges) {
         break;
       }
       y = edgelist[right].getCurve().getYTop();
       if (y > yrange[0]) {
         finalizeSubCurves(subcurves, chains);
       }
       yrange[0] = y;
     }
     // Incorporate new active edges that enter the active y range
     while (right < numedges) {
       e = edgelist[right];
       if (e.getCurve().getYTop() > y) {
         break;
       }
       right++;
     }
     // Sort the current active edges by their X values and
     // determine the maximum valid Y range where the X ordering
     // is correct
     yrange[1] = edgelist[left].getCurve().getYBot();
     if (right < numedges) {
       y = edgelist[right].getCurve().getYTop();
       if (yrange[1] > y) {
         yrange[1] = y;
       }
     }
     // Note: We could start at left+1, but we need to make
     // sure that edgelist[left] has its equivalence set to 0.
     int nexteq = 1;
     for (cur = left; cur < right; cur++) {
       e = edgelist[cur];
       e.setEquivalence(0);
       for (next = cur; next > left; next--) {
         Edge prevedge = edgelist[next - 1];
         int ordering = e.compareTo(prevedge, yrange);
         if (yrange[1] <= yrange[0]) {
           throw new RuntimeException("backstepping to " + yrange[1] + " from " + yrange[0]);
         }
         if (ordering >= 0) {
           if (ordering == 0) {
             // If the curves are equal, mark them to be
             // deleted later if they cancel each other
             // out so that we avoid having extraneous
             // curve segments.
             int eq = prevedge.getEquivalence();
             if (eq == 0) {
               eq = nexteq++;
               prevedge.setEquivalence(eq);
             }
             e.setEquivalence(eq);
           }
           break;
         }
         edgelist[next] = prevedge;
       }
       edgelist[next] = e;
     }
     // Now prune the active edge list.
     // For each edge in the list, determine its classification
     // (entering shape, exiting shape, ignore - no change) and
     // record the current Y range and its classification in the
     // Edge object for use later in constructing the new outline.
     newRow();
     double ystart = yrange[0];
     double yend = yrange[1];
     for (cur = left; cur < right; cur++) {
       e = edgelist[cur];
       int etag;
       int eq = e.getEquivalence();
       if (eq != 0) {
         // Find one of the segments in the "equal" range
         // with the right transition state and prefer an
         // edge that was either active up until ystart
         // or the edge that extends the furthest downward
         // (i.e. has the most potential for continuation)
         int origstate = getState();
         etag = (origstate == AreaOp.RSTAG_INSIDE ? AreaOp.ETAG_EXIT : AreaOp.ETAG_ENTER);
         Edge activematch = null;
         Edge longestmatch = e;
         double furthesty = yend;
         do {
           // Note: classify() must be called
           // on every edge we consume here.
           classify(e);
           if (activematch == null && e.isActiveFor(ystart, etag)) {
             activematch = e;
           }
           y = e.getCurve().getYBot();
           if (y > furthesty) {
             longestmatch = e;
             furthesty = y;
           }
         } while (++cur < right && (e = edgelist[cur]).getEquivalence() == eq);
         --cur;
         if (getState() == origstate) {
           etag = AreaOp.ETAG_IGNORE;
         } else {
           e = (activematch != null ? activematch : longestmatch);
         }
       } else {
         etag = classify(e);
       }
       if (etag != AreaOp.ETAG_IGNORE) {
         e.record(yend, etag);
         links.add(new CurveLink(e.getCurve(), ystart, yend, etag));
       }
     }
     // assert(getState() == AreaOp.RSTAG_OUTSIDE);
     if (getState() != AreaOp.RSTAG_OUTSIDE) {
       System.out.println("Still inside at end of active edge list!");
       System.out.println("num curves = " + (right - left));
       System.out.println("num links = " + links.size());
       System.out.println("y top = " + yrange[0]);
       if (right < numedges) {
         System.out.println("y top of next curve = " + edgelist[right].getCurve().getYTop());
       } else {
         System.out.println("no more curves");
       }
       for (cur = left; cur < right; cur++) {
         e = edgelist[cur];
         System.out.println(e);
         int eq = e.getEquivalence();
         if (eq != 0) {
           System.out.println("  was equal to " + eq + "...");
         }
       }
     }
     resolveLinks(subcurves, chains, links);
     links.clear();
     // Finally capture the bottom of the valid Y range as the top
     // of the next Y range.
     yrange[0] = yend;
   }
   finalizeSubCurves(subcurves, chains);
   Vector ret = new Vector();
   Enumeration enum_ = subcurves.elements();
   while (enum_.hasMoreElements()) {
     CurveLink link = (CurveLink) enum_.nextElement();
     ret.add(link.getMoveto());
     CurveLink nextlink = link;
     while ((nextlink = nextlink.getNext()) != null) {
       if (!link.absorb(nextlink)) {
         ret.add(link.getSubCurve());
         link = nextlink;
       }
     }
     ret.add(link.getSubCurve());
   }
   return ret;
 }