void generateSolventCube() {
		long time = System.currentTimeMillis();
		float rA, rB;
		Point3f ptA;
		Point3f ptY0 = new Point3f(), ptZ0 = new Point3f();
		Point3i pt0 = new Point3i(), pt1 = new Point3i();
		float maxValue = (dataType == SURFACE_NOMAP ? Float.MAX_VALUE : Float.MAX_VALUE);
		for (int x = 0; x < nPointsX; ++x)
			for (int y = 0; y < nPointsY; ++y)
				for (int z = 0; z < nPointsZ; ++z)
					voxelData[x][y][z] = maxValue;
		if (dataType == SURFACE_NOMAP)
			return;
		float maxRadius = 0;
		for (int iAtom = 0; iAtom < solvent_nAtoms; iAtom++) {
			ptA = solvent_ptAtom[iAtom];
			rA = solvent_atomRadius[iAtom];
			if (rA > maxRadius)
				maxRadius = rA;
			boolean isNearby = (iAtom >= solvent_firstNearbyAtom);
			setGridLimitsForAtom(ptA, rA, pt0, pt1);
			voxelPtToXYZ(pt0.x, pt0.y, pt0.z, ptXyzTemp);
			for (int i = pt0.x; i < pt1.x; i++) {
				ptY0.set(ptXyzTemp);
				for (int j = pt0.y; j < pt1.y; j++) {
					ptZ0.set(ptXyzTemp);
					for (int k = pt0.z; k < pt1.z; k++) {
						float v = ptXyzTemp.distance(ptA) - rA;
						if (v < voxelData[i][j][k])
							voxelData[i][j][k] = (isNearby ? Float.NaN : v);
						ptXyzTemp.add(volumetricVectors[2]);
					}
					ptXyzTemp.set(ptZ0);
					ptXyzTemp.add(volumetricVectors[1]);
				}
				ptXyzTemp.set(ptY0);
				ptXyzTemp.add(volumetricVectors[0]);
			}
		}
		if ((dataType == SURFACE_SOLVENT || dataType == SURFACE_MOLECULAR) && solventRadius > 0) {
			Point3i ptA0 = new Point3i();
			Point3i ptB0 = new Point3i();
			Point3i ptA1 = new Point3i();
			Point3i ptB1 = new Point3i();
			for (int iAtom = 0; iAtom < solvent_firstNearbyAtom - 1; iAtom++)
				if (solvent_ptAtom[iAtom] instanceof Atom) {
					ptA = solvent_ptAtom[iAtom];
					rA = solvent_atomRadius[iAtom] + solventRadius;
					setGridLimitsForAtom(ptA, rA - solventRadius, ptA0, ptA1);
					AtomIterator iter = frame.getWithinModelIterator((Atom) ptA, rA + solventRadius + maxRadius);
					while (iter.hasNext()) {
						Atom ptB = iter.next();
						if (ptB.atomIndex <= ((Atom) ptA).atomIndex)
							continue;
						// selected
						// only consider selected neighbors
						if (!bsSolventSelected.get(ptB.atomIndex))
							continue;
						rB = solventWorkingRadius(ptB) + solventRadius;
						if (solvent_quickPlane && thePlane != null
								&& Math.abs(distancePointToPlane(ptB, thePlane)) > 2 * rB)
							continue;

						float dAB = ptA.distance(ptB);
						if (dAB >= rA + rB)
							continue;
						// defining pt0 and pt1 very crudely -- this could be refined
						setGridLimitsForAtom(ptB, rB - solventRadius, ptB0, ptB1);
						pt0.x = Math.min(ptA0.x, ptB0.x);
						pt0.y = Math.min(ptA0.y, ptB0.y);
						pt0.z = Math.min(ptA0.z, ptB0.z);
						pt1.x = Math.max(ptA1.x, ptB1.x);
						pt1.y = Math.max(ptA1.y, ptB1.y);
						pt1.z = Math.max(ptA1.z, ptB1.z);
						voxelPtToXYZ(pt0.x, pt0.y, pt0.z, ptXyzTemp);
						for (int i = pt0.x; i < pt1.x; i++) {
							ptY0.set(ptXyzTemp);
							for (int j = pt0.y; j < pt1.y; j++) {
								ptZ0.set(ptXyzTemp);
								for (int k = pt0.z; k < pt1.z; k++) {
									float dVS = checkSpecialVoxel(ptA, rA, ptB, rB, dAB, ptXyzTemp);
									if (!Float.isNaN(dVS)) {
										float v = solventRadius - dVS;
										if (v < voxelData[i][j][k])
											voxelData[i][j][k] = v;
									}
									ptXyzTemp.add(volumetricVectors[2]);
								}
								ptXyzTemp.set(ptZ0);
								ptXyzTemp.add(volumetricVectors[1]);
							}
							ptXyzTemp.set(ptY0);
							ptXyzTemp.add(volumetricVectors[0]);
						}
					}
				}
		}
		if (thePlane != null) {
			maxValue = 0.001f;
			for (int x = 0; x < nPointsX; ++x)
				for (int y = 0; y < nPointsY; ++y)
					for (int z = 0; z < nPointsZ; ++z)
						if (voxelData[x][y][z] < maxValue) {
							// Float.NaN will also match ">=" this way
						}
						else {
							voxelData[x][y][z] = maxValue;
						}
		}
		Logger.debug("solvent surface time:" + (System.currentTimeMillis() - time));
	}