private static void renderHull(PtEntry pt) {
    Inf inf = Inf.create();
    int count = 30;
    PtEntry ptStart = pt;
    do {
      inf.update();
      if (pt.prev(true) != null) {
        boolean valley = false;

        valley =
            (pt.prev(true).source() == pt.source() && pt.prev(true).orig().next(true) != pt.orig());

        V.pushColor(MyColor.cBLUE);
        V.pushStroke(valley ? STRK_RUBBERBAND : STRK_THICK);
        V.drawLine(pt.prev(true), pt);
        V.pop(2);
      }
      V.pushColor(MyColor.cDARKGREEN);
      V.mark(pt, MARK_DISC, .6);
      V.pop();
      if (Editor.withLabels(true)) {
        StringBuilder sb = new StringBuilder();
        sb.append(" #" + pt.id());
        if (pt.source() != null) sb.append(" <" + pt.source() + ">");

        V.pushScale(.6);
        V.draw(sb.toString(), MyMath.ptOnCircle(pt, MyMath.radians(30), 3), TX_FRAME | TX_BGND);
        V.pop();
      }
      pt = pt.next(true);
      if (count-- == 0) V.draw("too many points rendering!", 50, 50, TX_CLAMP);
    } while (pt != ptStart && count > 0);
  }
 public String toString() {
   StringBuilder sb = new StringBuilder();
   sb.append("b<");
   sb.append(getLabel(0));
   sb.append(getLabel(1));
   sb.append('>');
   return sb.toString();
 }
  public String visString() {
    StringBuilder sb = new StringBuilder();
    if (visSeg != null) {
      for (int i = 0; i < visSeg.size(); i += 2) {
        double t0 = 0, t1 = 0;
        if (flipped()) {
          int j = visSeg.size() - i - 2;
          t0 = toExt(visSeg.getDouble(j + 1));
          t1 = toExt(visSeg.getDouble(j));
        } else {
          t0 = visSeg.getDouble(i);
          t1 = visSeg.getDouble(i + 1);
        }

        sb.append("<");
        sb.append(Tools.f(t0));
        sb.append("..");

        sb.append(Tools.f(t1));
        sb.append("> ");
      }
    }
    return sb.toString();
  }
  /**
   * Construct a string
   *
   * @param javaMode : if true, generates java code to construct it
   * @return String
   */
  private String toString(boolean javaMode) {

    StringBuilder sb = new StringBuilder();
    //    sb.setLength(0);

    if (javaMode) {
      sb.append(
          "new Hyperbola(new FPoint2("
              + foci[RIGHT].x
              + ","
              + foci[RIGHT].y
              + "), new FPoint2("
              + foci[LEFT].x
              + ","
              + foci[LEFT].y
              + "), new FPoint2("
              + pt.x
              + ","
              + pt.y
              + "));");
    } else {
      sb.append("hyperbola: ");
      if (!valid) {
        sb.append("***INVALID***");
      } else {
        sb.append(
            "f(r)="
                + foci[RIGHT]
                + " f(l)="
                + foci[LEFT]
                + " a="
                + Tools.f(a)
                + " c="
                + Tools.f(c));
        sb.append(" flip=" + Tools.f(flipped()));
        if (visSeg != null) {
          sb.append("\nVisible segments: ");
          for (int i = 0; i < visSeg.size(); i += 2) {
            double t0 = 0, t1 = 0;
            if (flipped()) {
              int j = visSeg.size() - i - 2;
              t0 = toExt(visSeg.getDouble(j + 1));
              t1 = toExt(visSeg.getDouble(j));
            } else {
              t0 = visSeg.getDouble(i);
              t1 = visSeg.getDouble(i + 1);
            }

            sb.append("<");
            sb.append(Tools.f(t0));
            sb.append("..");

            sb.append(Tools.f(t1));
            sb.append("> ");
          }
          sb.append("\n");
        }
      }
    }
    return sb.toString();
  }