void readVoxelData(boolean isMapData) throws Exception {
		/*
		 * possibilities:
		 * 
		 * cube file data only -- monochrome surface (single pass) cube file with plane (color, two pass) cube file data +
		 * cube file color data (two pass) jvxl file no color data (single pass) jvxl file with color data (single pass)
		 * jvxl file with plane (single pass)
		 * 
		 * 
		 */
		/*
		 * This routine is used twice in the case of color mapping. First (isMapData = false) to read the surface
		 * values, which might be a plane, then (isMapData = true) to color them based on a second data set.
		 * 
		 * Planes are only compatible with data sets that return actual numbers at all grid points -- cube files,
		 * orbitals, functionXY -- not solvent, spheres, elipsoids, or lobes.
		 * 
		 * It is possible to map a QM orbital onto a plane. In the first pass we defined the plane; in the second pass
		 * we just calculate the new voxel values and return.
		 * 
		 */
		boolean inside = false;
		int dataCount = 0;
		ichNextParse = 0;
		nThisValue = 0;
		surfaceData = "";
		nPointsX = voxelCounts[0];
		nPointsY = voxelCounts[1];
		nPointsZ = voxelCounts[2];
		int nPoints = nPointsX * nPointsY * nPointsZ;
		if (nPointsX <= 0 || nPointsY <= 0 || nPointsZ <= 0)
			return;
		if (!isSilent)
			Logger.debug("entering readVoxelData for fileIndex = " + fileIndex + "; " + nPoints
					+ " data points mapping=" + isMapData);

		// skip to the correct dataset and read the JVXL definition line if present
		// several JVXL variables and the plane will be defined here
		gotoData(fileIndex - 1, nPoints);

		thisInside = (!isJvxl || !isContoured);
		if (insideOut)
			thisInside = !thisInside;

		if (thePlane != null) {
			setPlaneParameters(thePlane);
			cutoff = 0f;
		}
		else if (isJvxl) {
			cutoff = (isBicolorMap || colorBySign ? 0.01f : 0.5f);
		}
		if (!isSilent)
			Logger.info("isosurface cutoff = " + cutoff);

		boolean justDefiningPlane = (!isMapData && thePlane != null);
		boolean isPrecalculation = (precalculateVoxelData && !justDefiningPlane);
		if (dataType == SURFACE_INFO) {
			if (justDefiningPlane) {
				voxelData = new float[nPointsX][][];
			}
			else {
				voxelData = tempVoxelData;
				tempVoxelData = null;
			}
		}
		else {
			voxelData = new float[nPointsX][][];
			if (isPrecalculation) {
				for (int x = 0; x < nPointsX; ++x) {
					voxelData[x] = new float[nPointsY][];
					for (int y = 0; y < nPointsY; ++y)
						voxelData[x][y] = new float[nPointsZ];
				}
				if (dataType == SURFACE_MOLECULARORBITAL)
					generateQuantumCube();
				else if (dataType == SURFACE_MEP)
					generateMepCube();
				else if ((dataType & IS_SOLVENTTYPE) != 0)
					generateSolventCube();
				else Logger.error("code error -- isPrecalculation, but how?");
				if (isMapData || thePlane != null)
					return;
			}
		}
		nDataPoints = 0;
		float zValue = 0;
		line = "";
		for (int x = 0; x < nPointsX; ++x) {
			float[][] plane;
			if (isPrecalculation) {
				plane = voxelData[x];
			}
			else {
				plane = new float[nPointsY][];
				voxelData[x] = plane;
			}
			for (int y = 0; y < nPointsY; ++y) {
				float[] strip;
				if (isPrecalculation) {
					strip = plane[y];
				}
				else {
					strip = new float[nPointsZ];
					plane[y] = strip;
				}
				if (dataType == SURFACE_FUNCTIONXY)
					zValue = getFunctionValue(x, y);
				for (int z = 0; z < nPointsZ; ++z) {
					float voxelValue;
					if (justDefiningPlane) {
						voxelValue = calcVoxelPlaneDistance(x, y, z);
					}
					else {
						switch (dataType) {
						case SURFACE_SPHERE:
						case SURFACE_ELLIPSOID:
							voxelValue = getSphereValue(x, y, z);
							break;
						case SURFACE_LOBE:
							voxelValue = getLobeValue(x, y, z);
							break;
						case SURFACE_MOLECULAR:
						case SURFACE_SOLVENT:
						case SURFACE_SASURFACE:
							if (isPrecalculation)
								voxelValue = strip[z]; // precalculated
							else
							// old way (for testing)
							voxelValue = getSolventValue(x, y, z);
							break;
						case SURFACE_ATOMICORBITAL:
							voxelValue = getPsi(x, y, z);
							break;
						case SURFACE_MOLECULARORBITAL:
						case SURFACE_MEP:
							voxelValue = strip[z]; // precalculated
							break;
						case SURFACE_FUNCTIONXY:
							voxelValue = (thePlane == null ? zValue - z : zValue);
							break;
						case SURFACE_INFO:
							voxelValue = voxelData[x][y][z];
							break;
						case SURFACE_NOMAP:
							voxelValue = 0f;
							break;
						case SURFACE_FILE:
						default:
							voxelValue = getNextVoxelValue();
						}
					}
					strip[z] = voxelValue;
					++nDataPoints;
					if (isJvxl && thePlane == null || isMapData)
						continue;

					// update surfaceData

					if (logCube)
						if (x < 20 && y < 20 && z < 20) {
							voxelPtToXYZ(x, y, z, ptXyzTemp);
							Logger.info("voxelData[" + x + "][" + y + "][" + z + "] xyz(Angstroms)=" + ptXyzTemp
									+ " value=" + voxelValue);
						}
					if (inside == isInside(voxelValue, cutoff)) {
						dataCount++;
					}
					else {
						if (dataCount != 0)
							surfaceData += " " + dataCount;
						dataCount = 1;
						inside = !inside;
					}
				}
			}
		}
		if (!isJvxl)
			surfaceData += " " + dataCount + "\n";
		if (!isMapData) {
			currentMesh.jvxlSurfaceData = (thePlane == null ? surfaceData : "");
			currentMesh.jvxlPlane = thePlane;
		}
		if (!isSilent)
			Logger.debug("Successfully read " + nPointsX + " x " + nPointsY + " x " + nPointsZ + " data points; "
					+ edgeCount + " edges");
	}