public SB getFileAsString(String entryName) {
   for (int i = 0; i < directory.size(); i++) {
     CompoundDocDirEntry thisEntry = directory.get(i);
     if (thisEntry.entryName.equals(entryName)) return getEntryAsString(thisEntry, false);
   }
   return new SB();
 }
 Object createAtomSeCollectionFromArrayData(
     List<Object> arrayData, Map<String, Object> htParams, boolean isAppend) {
   // NO STATE SCRIPT -- HERE WE ARE TRYING TO CONSERVE SPACE
   Logger.info("FileManager.getAtomSetCollectionFromArrayData(Vector)");
   int nModels = arrayData.size();
   String[] fullPathNames = new String[nModels];
   DataReader[] readers = new DataReader[nModels];
   for (int i = 0; i < nModels; i++) {
     fullPathNames[i] = "String[" + i + "]";
     readers[i] = newDataReader(arrayData.get(i));
   }
   JmolFilesReaderInterface filesReader =
       newFilesReader(fullPathNames, fullPathNames, null, readers, htParams, isAppend);
   filesReader.run();
   return filesReader.getAtomSetCollection();
 }
 /**
  * Sets all local file references in a script file to point to files within dataPath. If a file
  * reference contains dataPath, then the file reference is left with that RELATIVE path.
  * Otherwise, it is changed to a relative file name within that dataPath.
  *
  * <p>Only file references starting with "file://" are changed.
  *
  * @param script
  * @param dataPath
  * @param isLocal
  * @return revised script
  */
 private static String setScriptFileRefs(String script, String dataPath, boolean isLocal) {
   if (dataPath == null) return script;
   boolean noPath = (dataPath.length() == 0);
   List<String> fileNames = new List<String>();
   JmolBinary.getFileReferences(script, fileNames);
   List<String> oldFileNames = new List<String>();
   List<String> newFileNames = new List<String>();
   int nFiles = fileNames.size();
   for (int iFile = 0; iFile < nFiles; iFile++) {
     String name0 = fileNames.get(iFile);
     String name = name0;
     if (isLocal == isLocal(name)) {
       int pt = (noPath ? -1 : name.indexOf("/" + dataPath + "/"));
       if (pt >= 0) {
         name = name.substring(pt + 1);
       } else {
         pt = name.lastIndexOf("/");
         if (pt < 0 && !noPath) name = "/" + name;
         if (pt < 0 || noPath) pt++;
         name = dataPath + name.substring(pt);
       }
     }
     Logger.info("FileManager substituting " + name0 + " --> " + name);
     oldFileNames.addLast("\"" + name0 + "\"");
     newFileNames.addLast("\1\"" + name + "\"");
   }
   return Txt.replaceStrings(script, oldFileNames, newFileNames);
 }
 public String getDirectoryListing(String separator) {
   String str = "";
   for (int i = 0; i < directory.size(); i++) {
     CompoundDocDirEntry thisEntry = directory.get(i);
     if (!thisEntry.isEmpty)
       str +=
           separator
               + thisEntry.entryName
               + "\tlen="
               + thisEntry.lenStream
               + "\tSID="
               + thisEntry.SIDfirstSector
               + (thisEntry.isStandard
                   ? "\tfileOffset=" + getOffset(thisEntry.SIDfirstSector)
                   : "");
   }
   return str;
 }
 /**
  * reads a compound document directory and saves all data in a Hashtable so that the files may be
  * organized later in a different order. Also adds a #Directory_Listing entry.
  *
  * <p>Files are bracketed by BEGIN Directory Entry and END Directory Entry lines, similar to
  * ZipUtil.getAllData.
  *
  * @param prefix
  * @param binaryFileList |-separated list of files that should be saved as xx xx xx hex byte
  *     strings. The directory listing is appended with ":asBinaryString"
  * @param fileData
  */
 @Override
 public void getAllDataMapped(String prefix, String binaryFileList, Map<String, String> fileData) {
   fileData.put("#Directory_Listing", getDirectoryListing("|"));
   binaryFileList = "|" + binaryFileList + "|";
   for (int i = 0; i < directory.size(); i++) {
     CompoundDocDirEntry thisEntry = directory.get(i);
     if (!thisEntry.isEmpty && thisEntry.entryType != 5) {
       String name = thisEntry.entryName;
       Logger.info("CompoundDocument file " + name);
       boolean isBinary = (binaryFileList.indexOf("|" + name + "|") >= 0);
       if (isBinary) name += ":asBinaryString";
       SB data = new SB();
       data.append("BEGIN Directory Entry ").append(name).append("\n");
       data.appendSB(getEntryAsString(thisEntry, isBinary));
       data.append("\nEND Directory Entry ").append(name).append("\n");
       fileData.put(prefix + "/" + name, data.toString());
     }
   }
   close();
 }
 /**
  * We build two data structures for each slater:
  *
  * <p>int[] slaterInfo[] = {iatom, a, b, c, d} float[] slaterData[] = {zeta, coef}
  *
  * <p>where
  *
  * <p>psi = (coef)(x^a)(y^b)(z^c)(r^d)exp(-zeta*r)
  *
  * <p>Mopac: a == -2 ==> z^2 ==> (coef)(2z^2-x^2-y^2)(r^d)exp(-zeta*r) and: b == -2 ==>
  * (coef)(x^2-y^2)(r^d)exp(-zeta*r)
  *
  * @param iAtom
  * @param a
  * @param b
  * @param c
  * @param d
  * @param zeta
  * @param coef
  */
 protected final void addSlater(int iAtom, int a, int b, int c, int d, double zeta, float coef) {
   System.out.println(
       "SlaterReader "
           + slaters.size()
           + ": "
           + iAtom
           + " "
           + a
           + " "
           + b
           + " "
           + c
           + " "
           + d
           + " "
           + zeta
           + " "
           + coef);
   slaters.addLast(new SlaterData(iAtom, a, b, c, d, zeta, coef));
 }
 @Override
 public SB getAllDataFiles(String binaryFileList, String firstFile) {
   if (firstFile != null) {
     for (int i = 0; i < directory.size(); i++) {
       CompoundDocDirEntry thisEntry = directory.get(i);
       if (thisEntry.entryName.equals(firstFile)) {
         directory.remove(i);
         directory.add(1, thisEntry); // after ROOT_ENTRY
         break;
       }
     }
   }
   data = new SB();
   data.append("Compound Document File Directory: ");
   data.append(getDirectoryListing("|"));
   data.append("\n");
   binaryFileList = "|" + binaryFileList + "|";
   for (int i = 0; i < directory.size(); i++) {
     CompoundDocDirEntry thisEntry = directory.get(i);
     Logger.info("reading " + thisEntry.entryName);
     if (!thisEntry.isEmpty && thisEntry.entryType != 5) {
       String name = thisEntry.entryName;
       if (name.endsWith(".gz")) name = name.substring(0, name.length() - 3);
       data.append("BEGIN Directory Entry ").append(name).append("\n");
       data.appendSB(
           getEntryAsString(
               thisEntry, binaryFileList.indexOf("|" + thisEntry.entryName + "|") >= 0));
       data.append("\n");
       data.append("END Directory Entry ").append(thisEntry.entryName).append("\n");
     }
   }
   close();
   return data;
 }
 /**
  * after the vectors intinfo and floatinfo are completed, we
  *
  * @param doScale
  * @param doSort TODO
  */
 protected final void setSlaters(boolean doScale, boolean doSort) {
   if (slaterArray == null) {
     int nSlaters = slaters.size();
     slaterArray = new SlaterData[nSlaters];
     for (int i = 0; i < slaterArray.length; i++) slaterArray[i] = slaters.get(i);
   }
   if (doScale)
     for (int i = 0; i < slaterArray.length; i++) {
       SlaterData sd = slaterArray[i];
       sd.coef *= scaleSlater(sd.x, sd.y, sd.z, sd.r, sd.zeta);
       if (Logger.debugging) {
         Logger.debug(
             "SlaterReader "
                 + i
                 + ": "
                 + sd.iAtom
                 + " "
                 + sd.x
                 + " "
                 + sd.y
                 + " "
                 + sd.z
                 + " "
                 + sd.r
                 + " "
                 + sd.zeta
                 + " "
                 + sd.coef);
       }
     }
   if (doSort) {
     Arrays.sort(slaterArray, new SlaterSorter());
     int[] pointers = new int[slaterArray.length];
     for (int i = 0; i < slaterArray.length; i++) pointers[i] = slaterArray[i].index;
     sortOrbitalCoefficients(pointers);
   }
   moData.put("slaters", slaterArray);
   atomSetCollection.setAtomSetAuxiliaryInfo("moData", moData);
 }
 private void getDirectoryTable() {
   int thisSID = header.SID_DIR_start;
   CompoundDocDirEntry thisEntry;
   rootEntry = null;
   try {
     while (thisSID > 0) {
       gotoSector(thisSID);
       for (int j = nDirEntriesperSector; --j >= 0; ) {
         thisEntry = new CompoundDocDirEntry(this);
         thisEntry.readData();
         if (thisEntry.lenStream > 0) {
           directory.addLast(thisEntry);
           // System.out.println(thisEntry.entryName);
         }
         if (thisEntry.entryType == 5) rootEntry = thisEntry;
       }
       thisSID = SAT[thisSID];
     }
   } catch (Exception e) {
     Logger.errorEx(null, e);
   }
   if (Logger.debugging)
     Logger.debug("CompoundDocument directory entry: \n" + getDirectoryListing("\n"));
 }
  @Override
  protected void outputSurface(
      P3[] vertices,
      V3[] normals,
      short[] colixes,
      int[][] indices,
      short[] polygonColixes,
      int nVertices,
      int nPolygons,
      int nFaces,
      BS bsPolygons,
      int faceVertexMax,
      short colix,
      List<Short> colorList,
      Map<Short, Integer> htColixes,
      P3 offset) {
    if (polygonColixes != null) {
      boolean isAll = (bsPolygons == null);
      int i0 = (isAll ? nPolygons - 1 : bsPolygons.nextSetBit(0));
      for (int i = i0; i >= 0; i = (isAll ? i - 1 : bsPolygons.nextSetBit(i + 1))) {
        // if ((p++) % 10 == 0)
        //  output("\n");
        output("polygon { 4\n");
        for (int j = 0; j <= 3; j++) outputVertex(vertices[indices[i][j % 3]], offset);
        output("\n");
        output("pigment{rgbt<" + color4(colix = polygonColixes[i]) + ">}\n");
        output("  translucentFinish(" + translucencyFractionalFromColix(colix) + ")\n");
        output("  check_shadow()\n");
        output("  clip()\n");
        output("}\n");
      }
      return;
    }

    output("mesh2 {\n");
    output("vertex_vectors { " + nVertices);
    for (int i = 0; i < nVertices; i++) outputVertex(vertices[i], offset);
    output("\n}\n");

    boolean haveNormals = (normals != null);
    if (haveNormals) {
      output("normal_vectors { " + nVertices);
      for (int i = 0; i < nVertices; i++) {
        setTempVertex(vertices[i], offset, tempP2);
        output(getScreenNormal(tempP2, normals[i], 1));
        output("\n");
      }
      output("\n}\n");
    }

    if (colixes != null) {
      int nColix = colorList.size();
      output("texture_list { " + nColix);
      // just using the transparency of the first colix there...
      String finish =
          ">}" + " translucentFinish(" + translucencyFractionalFromColix(colixes[0]) + ")}";
      for (int i = 0; i < nColix; i++)
        output("\n, texture{pigment{rgbt<" + color4(colorList.get(i).shortValue()) + finish);
      output("\n}\n");
    }
    output("face_indices { " + nFaces);
    boolean isAll = (bsPolygons == null);
    int i0 = (isAll ? nPolygons - 1 : bsPolygons.nextSetBit(0));
    for (int i = i0; i >= 0; i = (isAll ? i - 1 : bsPolygons.nextSetBit(i + 1))) {
      output(", <" + getTriad(indices[i]) + ">");
      if (colixes != null) {
        output("," + htColixes.get(Short.valueOf(colixes[indices[i][0]])));
        output("," + htColixes.get(Short.valueOf(colixes[indices[i][1]])));
        output("," + htColixes.get(Short.valueOf(colixes[indices[i][2]])));
      }
      if (faceVertexMax == 4 && indices[i].length == 4) {
        output(", <" + indices[i][0] + "," + indices[i][2] + "," + indices[i][3] + ">");
        if (colixes != null) {
          output("," + htColixes.get(Short.valueOf(colixes[indices[i][0]])));
          output("," + htColixes.get(Short.valueOf(colixes[indices[i][2]])));
          output("," + htColixes.get(Short.valueOf(colixes[indices[i][3]])));
        }
      }
      output("\n");
    }
    output("\n}\n");

    if (colixes == null) {
      output("pigment{rgbt<" + color4(colix) + ">}\n");
      output("  translucentFinish(" + translucencyFractionalFromColix(colix) + ")\n");
    }
    output("  check_shadow()\n");
    output("  clip()\n");
    output("}\n");

    /*
    mesh2 {
    vertex_vectors {
    9,
    <0,0,0>, <0.5,0,0>, <0.5,0.5,0>,
    <1,0,0>, <1,0.5,0>, <1,1,0>
    <0.5,1,0>, <0,1,0>, <0,0.5,0>
    }
    normal_vectors {
    9,
    <-1,-1,0>, <0,-1,0>, <0,0,1>,
    <1,-1,0>, <1,0,0>, <1,1,0>,
    <0,1,0>, <-1,1,0>, <-1,0,0>
    }
    texture_list {
    3,
    texture{pigment{rgb <0,0,1>}},
    texture{pigment{rgb 1}},
    texture{pigment{rgb <1,0,0>}}
    }
    face_indices {
    8,
    <0,1,2>,0,1,2,  <1,3,2>,1,0,2,
    <3,4,2>,0,1,2,  <4,5,2>,1,0,2,
    <5,6,2>,0,1,2,  <6,7,2>,1,0,2,
    <7,8,2>,0,1,2,  <8,0,2>,1,0,2
    }
    }
    */

  }
  /**
   * @param distance a distance from a plane or point
   * @param plane a slabbing plane
   * @param ptCenters a set of atoms to measure distance from
   * @param vData when not null, this is a query, not an actual slabbing
   * @param fData vertex values or other data to overlay
   * @param bsSource TODO
   * @param meshSurface second surface; not implemented -- still some problems there
   * @param andCap to cap this off, crudely only
   * @param doClean compact set - draw only
   * @param tokType type of slab
   * @param isGhost translucent slab, so we mark slabbed triangles
   */
  public void getIntersection(
      float distance,
      P4 plane,
      P3[] ptCenters,
      List<P3[]> vData,
      float[] fData,
      BS bsSource,
      MeshSurface meshSurface,
      boolean andCap,
      boolean doClean,
      int tokType,
      boolean isGhost) {
    boolean isSlab = (vData == null);
    P3[] pts = null;
    if (fData == null) {
      if (tokType == T.decimal && bsSource != null) {
        fData = new float[vertexCount];
        for (int i = 0; i < vertexCount; i++)
          if ((fData[i] = vertexSource[i]) == -1) System.out.println("meshsurface hmm");
      } else {
        fData = vertexValues;
      }
    }
    Map<String, Integer> mapEdge = new Hashtable<String, Integer>();

    /*
    Vector3f vNorm = null;
    Vector3f vBC = null;
    Vector3f vAC = null;
    Point3f[] pts2 = null;
    Vector3f vTemp3 = null;
    Point4f planeTemp = null;
    boolean isMeshIntersect = (meshSurface != null);
    if (isMeshIntersect) {
      // NOT IMPLEMENTED
      vBC = new Vector3f();
      vAC = new Vector3f();
      vNorm = new Vector3f();
      plane = new Point4f();
      planeTemp = new Point4f();
      vTemp3 = new Vector3f();
      pts2 = new Point3f[] { null, new Point3f(), new Point3f() };
    }
    */
    if (ptCenters != null || isGhost)
      andCap = false; // can only cap faces, and no capping of ghosts
    float[] values = new float[2];
    float[] fracs = new float[2];
    double absD = Math.abs(distance);
    float d1, d2, d3, valA, valB, valC;
    int sourceA = 0, sourceB = 0, sourceC = 0, setA = 0;
    List<int[]> iPts = (andCap ? new List<int[]>() : null);
    if (polygonCount == 0) {
      for (int i = mergeVertexCount0; i < vertexCount; i++) {
        if (Float.isNaN(fData[i])
            || checkSlab(tokType, vertices[i], fData[i], distance, plane, ptCenters, bsSource) > 0)
          bsSlabDisplay.clear(i);
      }
      return;
    }
    int iLast = polygonCount;
    for (int i = mergePolygonCount0; i < iLast; i++) {
      if (!setABC(i)) continue;
      BS bsSlab = (bsSlabGhost != null && bsSlabGhost.get(i) ? bsSlabGhost : bsSlabDisplay);
      int check1 = polygonIndexes[i][3];
      int check2 = (checkCount == 2 ? polygonIndexes[i][4] : 0);
      P3 vA = vertices[iA];
      P3 vB = vertices[iB];
      P3 vC = vertices[iC];
      valA = fData[iA];
      valB = fData[iB];
      valC = fData[iC];
      if (vertexSource != null) {
        sourceA = vertexSource[iA];
        sourceB = vertexSource[iB];
        sourceC = vertexSource[iC];
      }
      if (vertexSets != null) setA = vertexSets[iA];
      d1 =
          checkSlab(
              tokType,
              vA,
              valA,
              (bsSource == null ? distance : sourceA),
              plane,
              ptCenters,
              bsSource);
      d2 =
          checkSlab(
              tokType,
              vB,
              valB,
              (bsSource == null ? distance : sourceB),
              plane,
              ptCenters,
              bsSource);
      d3 =
          checkSlab(
              tokType,
              vC,
              valC,
              (bsSource == null ? distance : sourceC),
              plane,
              ptCenters,
              bsSource);
      int test1 =
          (d1 != 0 && d1 < 0 ? 1 : 0) + (d2 != 0 && d2 < 0 ? 2 : 0) + (d3 != 0 && d3 < 0 ? 4 : 0);

      /*
            if (iA == 955 || iB == 955 || iC == 955) {
              System.out.println(i + " " + iA + " " + iB + " " + iC + " "+ d1 + " " + d2 + " " + d3 + " " + test1);
              System.out.println("testing messhsurf ");
            }
      */
      /*
      if (isMeshIntersect && test1 != 7 && test1 != 0) {
        // NOT IMPLEMENTED
        boolean isOK = (d1 == 0 && d2 * d3 >= 0 || d2 == 0 && (d1 * d3) >= 0 || d3 == 0
            && d1 * d2 >= 0);
        if (isOK)
          continue;
        // We have a potential crossing. Now to find the exact point of crossing
        // the other isosurface.
        if (checkIntersection(vA, vB, vC, meshSurface, pts2, vNorm, vBC, vAC,
            plane, planeTemp, vTemp3)) {
          iD = addIntersectionVertex(pts2[0], 0, sourceA, mapEdge, -1, -1); // have to choose some source
          addPolygon(iA, iB, iD, check1 & 1, check2, 0, bsSlabDisplay);
          addPolygon(iD, iB, iC, check1 & 2, check2, 0, bsSlabDisplay);
          addPolygon(iA, iD, iC, check1 & 4, check2, 0, bsSlabDisplay);
          test1 = 0; // toss original
          iLast = polygonCount;
        } else {
          // process normally for now
          // not fully implemented -- need to check other way as well.
        }
      }
      */
      switch (test1) {
        default:
        case 7:
        case 0:
          // all on the same side
          break;
        case 1:
        case 6:
          // BC on same side
          if (ptCenters == null)
            pts =
                new P3[] {
                  interpolatePoint(vA, vB, -d1, d2, valA, valB, values, fracs, 0),
                  interpolatePoint(vA, vC, -d1, d3, valA, valC, values, fracs, 1)
                };
          else
            pts =
                new P3[] {
                  interpolateSphere(vA, vB, -d1, -d2, absD, valA, valB, values, fracs, 0),
                  interpolateSphere(vA, vC, -d1, -d3, absD, valA, valC, values, fracs, 1)
                };
          break;
        case 2:
        case 5:
          // AC on same side
          if (ptCenters == null)
            pts =
                new P3[] {
                  interpolatePoint(vB, vA, -d2, d1, valB, valA, values, fracs, 1),
                  interpolatePoint(vB, vC, -d2, d3, valB, valC, values, fracs, 0)
                };
          else
            pts =
                new P3[] {
                  interpolateSphere(vB, vA, -d2, -d1, absD, valB, valA, values, fracs, 1),
                  interpolateSphere(vB, vC, -d2, -d3, absD, valB, valC, values, fracs, 0)
                };
          break;
        case 3:
        case 4:
          // AB on same side need A-C, B-C
          if (ptCenters == null)
            pts =
                new P3[] {
                  interpolatePoint(vC, vA, -d3, d1, valC, valA, values, fracs, 0),
                  interpolatePoint(vC, vB, -d3, d2, valC, valB, values, fracs, 1)
                };
          else
            pts =
                new P3[] {
                  interpolateSphere(vC, vA, -d3, -d1, absD, valC, valA, values, fracs, 0),
                  interpolateSphere(vC, vB, -d3, -d2, absD, valC, valB, values, fracs, 1)
                };
          break;
      }
      doClear = true;
      doGhost = isGhost;
      doCap = andCap;
      BS bs;
      // adjust for minor discrepencies
      // for (int j = 0; j < 2; j++)
      // if (fracs[j] == 0)
      // fracs[1 - j] = (fracs[1 - j] < 0.5 ? 0 : 1);

      if (isSlab) {
        //        iD = iE = -1;
        switch (test1) {
            //             A
            //            / \
            //           B---C
          case 0:
            // all on the same side -- toss
            doCap = false;
            break;
          case 7:
            // all on the same side -- keep
            continue;
          case 1:
          case 6:
            //          0  A  0
            //            / \
            //        0 -------- 1
            //          /     \
            //       1 B-------C  1
            boolean tossBC = (test1 == 1);
            if (tossBC || isGhost) {
              // 1: BC on side to toss -- +tossBC+isGhost  -tossBC+isGhost
              if (!getDE(fracs, 0, iA, iB, iC, tossBC)) break;
              if (iD < 0)
                iD = addIntersectionVertex(pts[0], values[0], sourceA, setA, mapEdge, iA, iB);
              if (iE < 0)
                iE = addIntersectionVertex(pts[1], values[1], sourceA, setA, mapEdge, iA, iC);
              bs = (tossBC ? bsSlab : bsSlabGhost);
              addPolygonV3(iA, iD, iE, check1 & 5 | 2, check2, 0, bs);
              if (!isGhost) break;
            }
            // BC on side to keep -- -tossBC+isGhost,  +tossBC+isGhost
            if (!getDE(fracs, 1, iA, iC, iB, tossBC)) break;
            bs = (tossBC ? bsSlabGhost : bsSlab);
            if (iE < 0) {
              iE = addIntersectionVertex(pts[0], values[0], sourceB, setA, mapEdge, iA, iB);
              addPolygonV3(iE, iB, iC, check1 & 3, check2, 0, bs);
            }
            if (iD < 0) {
              iD = addIntersectionVertex(pts[1], values[1], sourceC, setA, mapEdge, iA, iC);
              addPolygonV3(iD, iE, iC, check1 & 4 | 1, check2, 0, bs);
            }
            break;
          case 5:
          case 2:
            //              A
            //            \/ \
            //            /\  \
            //           B--\--C
            //               \
            //
            boolean tossAC = (test1 == 2);
            if (tossAC || isGhost) {
              // AC on side to toss
              if (!getDE(fracs, 0, iB, iC, iA, tossAC)) break;
              bs = (tossAC ? bsSlab : bsSlabGhost);
              if (iE < 0)
                iE = addIntersectionVertex(pts[0], values[0], sourceB, setA, mapEdge, iB, iA);
              if (iD < 0)
                iD = addIntersectionVertex(pts[1], values[1], sourceB, setA, mapEdge, iB, iC);
              addPolygonV3(iE, iB, iD, check1 & 3 | 4, check2, 0, bs);
              if (!isGhost) break;
            }
            // AC on side to keep
            if (!getDE(fracs, 1, iB, iA, iC, tossAC)) break;
            bs = (tossAC ? bsSlabGhost : bsSlab);
            if (iD < 0) {
              iD = addIntersectionVertex(pts[0], values[0], sourceA, setA, mapEdge, iB, iA);
              addPolygonV3(iA, iD, iC, check1 & 5, check2, 0, bs);
            }
            if (iE < 0) {
              iE = addIntersectionVertex(pts[1], values[1], sourceC, setA, mapEdge, iB, iC);
              addPolygonV3(iD, iE, iC, check1 & 2 | 1, check2, 0, bs);
            }
            break;
          case 4:
          case 3:
            //              A
            //             / \/
            //            /  /\
            //           B--/--C
            //             /
            //
            boolean tossAB = (test1 == 4);
            if (tossAB || isGhost) {
              if (!getDE(fracs, 0, iC, iA, iB, tossAB)) break;
              if (iD < 0)
                iD = addIntersectionVertex(pts[0], values[0], sourceC, setA, mapEdge, iA, iC); // CA
              if (iE < 0)
                iE = addIntersectionVertex(pts[1], values[1], sourceC, setA, mapEdge, iB, iC); // CB
              bs = (tossAB ? bsSlab : bsSlabGhost);
              addPolygonV3(iD, iE, iC, check1 & 6 | 1, check2, 0, bs);
              if (!isGhost) break;
            }
            // AB on side to keep
            if (!getDE(fracs, 1, iC, iB, iA, tossAB)) break;
            bs = (tossAB ? bsSlabGhost : bsSlab);
            if (iE < 0) {
              iE = addIntersectionVertex(pts[0], values[0], sourceA, setA, mapEdge, iA, iC); // CA
              addPolygonV3(iA, iB, iE, check1 & 5, check2, 0, bs);
            }
            if (iD < 0) {
              iD = addIntersectionVertex(pts[1], values[1], sourceB, setA, mapEdge, iB, iC); // CB
              addPolygonV3(iE, iB, iD, check1 & 2 | 4, check2, 0, bs);
            }
            break;
        }
        if (doClear) {
          bsSlab.clear(i);
          if (doGhost) bsSlabGhost.set(i);
        }
        if (doCap) {
          iPts.addLast(new int[] {iD, iE});
        }
      } else if (pts != null) {
        vData.addLast(pts);
      }
    }
    if (andCap && iPts.size() > 0) {
      P3 center = new P3();
      for (int i = iPts.size(); --i >= 0; ) {
        int[] ipts = iPts.get(i);
        center.add(vertices[ipts[0]]);
        center.add(vertices[ipts[1]]);
      }
      center.scale(0.5f / iPts.size());
      int v0 = addIntersectionVertex(center, 0, -1, setA, mapEdge, -1, -1);
      for (int i = iPts.size(); --i >= 0; ) {
        int[] ipts = iPts.get(i);
        // int p =
        addPolygonV3(ipts[0], v0, ipts[1], 0, 0, 0, bsSlabDisplay);
      }
    }

    if (!doClean) return;
    BS bsv = new BS();
    BS bsp = new BS();
    for (int i = 0; i < polygonCount; i++) {
      if (polygonIndexes[i] == null) continue;
      bsp.set(i);
      for (int j = 0; j < 3; j++) bsv.set(polygonIndexes[i][j]);
    }
    int n = 0;
    int nPoly = bsp.cardinality();
    if (nPoly != polygonCount) {
      int[] map = new int[vertexCount];
      for (int i = 0; i < vertexCount; i++) if (bsv.get(i)) map[i] = n++;
      P3[] vTemp = new P3[n];
      n = 0;
      for (int i = 0; i < vertexCount; i++) if (bsv.get(i)) vTemp[n++] = vertices[i];
      int[][] pTemp = AU.newInt2(nPoly);
      nPoly = 0;
      for (int i = 0; i < polygonCount; i++)
        if (polygonIndexes[i] != null) {
          for (int j = 0; j < 3; j++) polygonIndexes[i][j] = map[polygonIndexes[i][j]];
          pTemp[nPoly++] = polygonIndexes[i];
        }
      vertices = vTemp;
      vertexCount = n;
      polygonIndexes = pTemp;
      polygonCount = nPoly;
    }
  }
 public void slabPolygonsList(List<Object[]> slabInfo, boolean allowCap) {
   for (int i = 0; i < slabInfo.size(); i++) if (!slabPolygons(slabInfo.get(i), allowCap)) break;
 }
 protected void addSlater(SlaterData sd, int n) {
   sd.index = n;
   slaters.addLast(sd);
 }