void setProperty(String propertyName, Object value, BitSet bs) {

		Logger.debug("Isosurface state=" + state + " setProperty: " + propertyName + " = " + value);

		if ("init" == propertyName) {
			script = (String) value;
			initializeIsosurface();
			if (!(iHaveBitSets = getScriptBitSets()))
				bsSelected = bs; // THIS MAY BE NULL
			super.setProperty("thisID", null, null);
			return;
		}

		if ("title" == propertyName) {
			if (value == null) {
				title = null;
				return;
			}
			else if (value instanceof String[]) {
				title = (String[]) value;
			}
			else {
				int nLine = 1;
				String lines = (String) value;
				for (int i = lines.length(); --i >= 0;)
					if (lines.charAt(i) == '|')
						nLine++;
				title = new String[nLine];
				nLine = 0;
				int i0 = -1;
				for (int i = 0; i < lines.length(); i++)
					if (lines.charAt(i) == '|') {
						title[nLine++] = lines.substring(i0 + 1, i);
						i0 = i;
					}
				title[nLine] = lines.substring(i0 + 1);
			}
			for (int i = 0; i < title.length; i++)
				if (title[i].length() > 0)
					Logger.info("TITLE " + title[i]);
			return;
		}

		if ("debug" == propertyName) {
			boolean TF = ((Boolean) value).booleanValue();
			// Logger.setActiveLevel(Logger.LEVEL_DEBUG, TF);
			logMessages = TF;
			// logCompression = TF;
			logCube = TF;
			return;
		}

		if ("select" == propertyName) {
			if (!iHaveBitSets)
				bsSelected = (BitSet) value;
			return;
		}

		if ("ignore" == propertyName) {
			if (!iHaveBitSets)
				bsIgnore = (BitSet) value;
			return;
		}

		if ("cutoff" == propertyName) {
			cutoff = ((Float) value).floatValue();
			isPositiveOnly = false;
			return;
		}

		if ("cutoffPositive" == propertyName) {
			cutoff = ((Float) value).floatValue();
			isPositiveOnly = true;
			return;
		}

		if ("scale" == propertyName) {
			scale = ((Float) value).floatValue();
			return;
		}

		if ("angstroms" == propertyName) {
			isAngstroms = true;
			return;
		}

		if ("center" == propertyName) {
			center.set((Point3f) value);
			return;
		}

		if ("resolution" == propertyName) {
			resolution = ((Float) value).floatValue();
			if (resolution == 0)
				resolution = getDefaultResolution();
			return;
		}

		if ("anisotropy" == propertyName) {
			if ((dataType & NO_ANISOTROPY) != 0)
				return;
			Point3f pt = (Point3f) value;
			anisotropy[0] = pt.x;
			anisotropy[1] = pt.y;
			anisotropy[2] = pt.z;
			isAnisotropic = true;
			return;
		}

		if ("eccentricity" == propertyName) {
			setEccentricity((Point4f) value);
			return;
		}

		if ("addHydrogens" == propertyName) {
			addHydrogens = ((Boolean) value).booleanValue();
			return;
		}

		// / special effects

		if ("gridPoints" == propertyName) {
			iAddGridPoints = true;
			return;
		}

		if ("fixed" == propertyName) {
			isFixed = ((Boolean) value).booleanValue();
			setModelIndex();
			return;
		}

		if ("atomIndex" == propertyName) {
			atomIndex = ((Integer) value).intValue();
			return;
		}

		// / hidden options

		if ("fileIndex" == propertyName) {
			fileIndex = ((Integer) value).intValue();
			if (fileIndex < 1)
				fileIndex = 1;
			return;
		}

		if ("insideOut" == propertyName) {
			insideOut = true;
			return;
		}

		if ("remappable" == propertyName) {
			jvxlWritePrecisionColor = true;
			return;
		}

		// / color options

		if ("sign" == propertyName) {
			isCutoffAbsolute = true;
			colorBySign = true;
			colorPtr = 0;
			return;
		}

		if ("colorRGB" == propertyName) {
			int rgb = ((Integer) value).intValue();
			colorPos = colorPosLCAO = rgb;
			defaultColix = Graphics3D.getColix(rgb);
			if (colorPtr++ == 0)
				colorNeg = colorNegLCAO = rgb;
			return;
		}

		if ("red" == propertyName) {
			valueMappedToRed = ((Float) value).floatValue();
			return;
		}

		if ("blue" == propertyName) {
			valueMappedToBlue = ((Float) value).floatValue();
			rangeDefined = true;
			return;
		}

		if ("reverseColor" == propertyName) {
			isColorReversed = true;
			return;
		}

		if ("setColorScheme" == propertyName) {
			colorScheme = ((String) value);
			if (currentMesh != null && colorScheme.equals("sets")) {
				currentMesh.surfaceSet = getSurfaceSet(0);
				super.setProperty("color", "sets", null);
			}
			return;
		}

		if ("contour" == propertyName) {
			isContoured = true;
			int n = ((Integer) value).intValue();
			if (n > 0)
				nContours = n;
			else if (n == 0)
				nContours = defaultContourCount;
			else thisContour = -n;
			return;
		}

		if ("phase" == propertyName) {
			String color = (String) value;
			isCutoffAbsolute = true;
			colorBySign = true;
			colorByPhase = true;
			colorPhase = -1;
			for (int i = colorPhases.length; --i >= 0;)
				if (color.equalsIgnoreCase(colorPhases[i])) {
					colorPhase = i;
					break;
				}
			if (colorPhase < 0) {
				Logger.warn(" invalid color phase: " + color);
				colorPhase = 1;
			}
			if (logMessages)
				Logger.info("phase " + color + " " + colorPhase);
			if (state == STATE_DATA_READ) {
				dataType = surfaceType;
				state = STATE_DATA_COLORED;
				if (currentMesh != null)
					applyColorScale(currentMesh);
			}
			return;
		}

		if ("map" == propertyName) {
			if (currentMesh != null)
				state = STATE_DATA_READ;
			return;
		}

		// / final actions ///

		if ("plane" == propertyName) {
			thePlane = (Point4f) value;
			isContoured = true;
			++state;
			return;
		}

		if ("sphere" == propertyName) {
			sphere_radiusAngstroms = ((Float) value).floatValue();
			dataType = SURFACE_SPHERE;
			isSilent = !logMessages;
			setEccentricity(new Point4f(0, 0, 1, 1));
			cutoff = Float.MIN_VALUE;
			script = " center " + StateManager.escape(center) + " SPHERE " + sphere_radiusAngstroms;
			propertyName = "getSurface";
		}

		if ("ellipsoid" == propertyName) {
			Point4f v = (Point4f) value;
			setEccentricity(v);
			dataType = SURFACE_ELLIPSOID;
			sphere_radiusAngstroms = 1.0f;
			cutoff = Float.MIN_VALUE;
			propertyName = "getSurface";
			script = " center " + StateManager.escape(center) + (Float.isNaN(scale) ? "" : " scale " + scale)
					+ " ELLIPSOID {" + v.x + " " + v.y + " " + v.z + " " + v.w + "}";
		}

		if ("lobe" == propertyName) {
			Point4f v = (Point4f) value;
			setEccentricity(v);
			dataType = SURFACE_LOBE;
			if (cutoff == Float.MAX_VALUE)
				cutoff = defaultOrbitalCutoff;
			script = " center " + StateManager.escape(center) + (Float.isNaN(scale) ? "" : " scale " + scale)
					+ " LOBE {" + v.x + " " + v.y + " " + v.z + " " + v.w + "}";
			propertyName = "getSurface";
		}

		if ("lcaoType" == propertyName) {
			lcaoType = (String) value;
			if (colorPtr == 1)
				colorPosLCAO = colorNegLCAO;
			isSilent = !logMessages;
			return;
		}

		if ("lcaoCartoon" == propertyName) {
			if (++state != STATE_DATA_READ)
				return;
			Vector3f[] info = (Vector3f[]) value;
			// z x center
			if (center.x == Float.MAX_VALUE)
				center.set(info[2]);
			drawLcaoCartoon(lcaoType, info[0], info[1]);
		}

		/*
		 * Based on the form of the parameters, returns and encoded radius as follows:
		 * 
		 * script meaning range encoded
		 * 
		 * +1.2 offset [0 - 10] x -1.2 offset 0) x 1.2 absolute (0 - 10] x + 10 -30% 70% (-100 - 0) x + 200 +30% 130% (0
		 * x + 200 80% percent (0 x + 100
		 * 
		 * in each case, numbers can be integer or float
		 * 
		 */

		if ("vdwRadius" == propertyName || "ionicRadius" == propertyName) {
			useIonic = (propertyName.charAt(0) == 'i');
			float radius = ((Float) value).floatValue();
			if (radius >= 100)
				solventAtomRadiusFactor = (radius - 100) / 100;
			else if (radius > 10)
				solventAtomRadiusAbsolute = radius - 10;
			else solventAtomRadiusOffset = radius;
			return;
		}

		if ("molecular" == propertyName || "solvent" == propertyName || "sasurface" == propertyName
				|| "nomap" == propertyName) {
			// plain plane
			isEccentric = isAnisotropic = false;
			// anisotropy[0] = anisotropy[1] = anisotropy[2] = 1f;
			solventRadius = ((Float) value).floatValue();
			if (solventRadius < 0)
				solventRadius = defaultSolventRadius;
			dataType = ("nomap" == propertyName ? SURFACE_NOMAP : "molecular" == propertyName ? SURFACE_MOLECULAR
					: "sasurface" == propertyName || solventRadius == 0f ? SURFACE_SASURFACE : SURFACE_SOLVENT);
			switch (dataType) {
			case SURFACE_NOMAP:
				solventExtendedAtomRadius = solventRadius;
				solventRadius = 0f;
				isContoured = false;
				break;
			case SURFACE_MOLECULAR:
				solventExtendedAtomRadius = 0f;
				Logger.info("creating molecular surface with radius " + solventRadius);
				break;
			case SURFACE_SOLVENT:
				solventExtendedAtomRadius = 0f;
				if (bsIgnore == null)
					bsIgnore = viewer.getAtomBitSet("(solvent)");
				Logger.info("creating solvent-excluded surface with radius " + solventRadius);
				break;
			case SURFACE_SASURFACE:
				solventExtendedAtomRadius = solventRadius;
				solventRadius = 0f;
				if (bsIgnore == null)
					bsIgnore = viewer.getAtomBitSet("(solvent)");
				Logger.info("creating solvent-accessible surface with radius " + solventExtendedAtomRadius);
				break;
			}
			if (state == STATE_DATA_READ) {
				propertyName = "mapColor";
			}
			else {
				cutoff = 0.0f;
				propertyName = "getSurface";
			}
		}

		if ("moData" == propertyName) {
			moData = (Hashtable) value;
			propertyName = "mapColor";
			return;
		}

		if ("molecularOrbital" == propertyName) {
			qm_moNumber = ((Integer) value).intValue();
			qmOrbitalType = (moData.containsKey("gaussians") ? QM_TYPE_GAUSSIAN
					: moData.containsKey("slaterInfo") ? QM_TYPE_SLATER : QM_TYPE_UNKNOWN);
			if (qmOrbitalType == QM_TYPE_UNKNOWN) {
				Logger.error("moData does not contain data of a known type");
				return;
			}
			Vector mos = (Vector) (moData.get("mos"));
			qmOrbitalCount = mos.size();
			Logger.info("Molecular orbital #" + qm_moNumber + "/" + qmOrbitalCount + " "
					+ moData.get("calculationType"));
			Hashtable mo = (Hashtable) mos.get(qm_moNumber - 1);
			if (title == null) {
				title = new String[5];
				title[0] = "%F";
				title[1] = "Model %M  MO %I/%N";
				title[2] = "Energy = %E %U";
				title[3] = "?Symmetry = %S";
				title[4] = "?Occupancy = %O";
			}

			for (int i = title.length; --i >= 0;)
				addMOTitleInfo(i, mo);
			moCoefficients = (float[]) mo.get("coefficients");
			dataType = SURFACE_MOLECULARORBITAL;
			if (state == STATE_DATA_READ) {
				propertyName = "mapColor";
			}
			else {
				colorBySign = true;
				colorByPhase = true;
				colorPhase = 0;
				if (cutoff == Float.MAX_VALUE)
					cutoff = defaultQMOrbitalCutoff;
				isCutoffAbsolute = (cutoff > 0 && !isPositiveOnly);
				isBicolorMap = true;
				propertyName = "getSurface";
			}
		}

		if ("mep" == propertyName) {
			mepCharges = (float[]) value;
			isEccentric = isAnisotropic = false;
			dataType = SURFACE_MEP;
			if (state == STATE_DATA_READ) {
				if (!rangeDefined) {
					valueMappedToRed = defaultMepMin;
					valueMappedToBlue = defaultMepMax;
					rangeDefined = true;
				}
				propertyName = "mapColor";
			}
			else {
				colorBySign = true;
				colorByPhase = true;
				colorPhase = 0;
				if (cutoff == Float.MAX_VALUE)
					cutoff = defaultMepCutoff;
				isCutoffAbsolute = (cutoff > 0 && !isPositiveOnly);
				isBicolorMap = true;
				propertyName = "getSurface";
			}
		}

		if ("hydrogenOrbital" == propertyName) {
			dataType = SURFACE_ATOMICORBITAL;
			float[] nlmZ = (float[]) value;
			psi_n = (int) nlmZ[0];
			psi_l = (int) nlmZ[1];
			psi_m = (int) nlmZ[2];
			psi_Znuc = nlmZ[3];
			psi_ptsPerAngstrom = 10;
			// quantum rule is abs(m) <= l < n
			if (psi_Znuc <= 0 || Math.abs(psi_m) > psi_l || psi_l >= psi_n) {
				Logger.error("must have |m| <= l < n and Znuc > 0: " + psi_n + " " + psi_l + " " + psi_m + " "
						+ psi_Znuc);
				return;
			}
			if (state == STATE_DATA_READ) {
				propertyName = "mapColor";
			}
			else {
				if (cutoff == Float.MAX_VALUE)
					cutoff = defaultOrbitalCutoff;
				isCutoffAbsolute = true;
				if (colorBySign)
					isBicolorMap = true;
				propertyName = "getSurface";
			}
		}

		if ("functionXY" == propertyName) {
			dataType = SURFACE_FUNCTIONXY;
			functionXYinfo = (Vector) value;
			// if (state == STATE_DATA_READ) { for now, mapping function not allowed
			// propertyName = "mapColor";
			// } else {
			// isContoured = false;
			// I think the problem is that there's no direct way of knowing
			// that a function is being used with contours. And then, if
			// it is contoured, it should not be displayed.
			if (isContoured)
				setPlaneParameters(new Point4f(0, 0, 1, 0)); // xy plane through origin
			cutoff = Float.MIN_VALUE;
			isEccentric = isAnisotropic = false;
			propertyName = "getSurface";
			// }
		}

		if ("getSurface" == propertyName) {
			if (++state != STATE_DATA_READ)
				return;
			if (dataType == SURFACE_NONE) {
				if (value instanceof BufferedReader) {
					br = (BufferedReader) value;
					dataType = SURFACE_FILE;
				}
				else if (value instanceof Hashtable) {
					surfaceInfo = (Hashtable) value;
					dataType = SURFACE_INFO;
				}
				else {
					Logger.error("unknown surface data type??");
					return;
				}
				if (colorBySign)
					isBicolorMap = true;
			}
			surfaceType = dataType;
			if (!isSilent)
				Logger.info("loading voxel data...");
			checkFlags();
			long timeBegin = System.currentTimeMillis();
			if (!createIsosurface()) {
				Logger.error("Could not create isosurface");
				return;
			}
			if (!isSilent)
				Logger.info("surface calculation time: " + (System.currentTimeMillis() - timeBegin) + " ms");
			initializeMesh(force2SidedTriangles);
			jvxlFileMessage = (jvxlDataIsColorMapped ? "mapped" : "");
			if (isContoured && thePlane == null) {
				planarVectors[0].set(volumetricVectors[0]);
				planarVectors[1].set(volumetricVectors[1]);
				pixelCounts[0] = voxelCounts[0];
				pixelCounts[1] = voxelCounts[1];
			}
			if (jvxlDataIs2dContour)
				colorIsosurface();
			currentMesh.nBytes = nBytes;
			if (colorByPhase || colorBySign) {
				state = STATE_DATA_COLORED;
				mappingType = dataType;
				applyColorScale(currentMesh);
			}
			if (colorScheme.equals("sets")) {
				currentMesh.surfaceSet = getSurfaceSet(0);
				super.setProperty("color", "sets", null);
			}
			setModelIndex();
			if (logMessages && thePlane == null && !isSilent)
				Logger.debug("\n" + jvxlGetFile(currentMesh, jvxlFileMessage, true, 1));
			discardTempData(jvxlDataIs2dContour);
			dataType = SURFACE_NONE;
			mappedDataMin = Float.MAX_VALUE;
			return;
		}

		if ("mapColor" == propertyName) {
			if (++state != STATE_DATA_COLORED)
				return;
			if (!isSilent)
				Logger.info("mapping data...");
			if (dataType == SURFACE_NONE) {
				if (value instanceof BufferedReader) {
					br = (BufferedReader) value;
					dataType = SURFACE_FILE;
				}
				else if (value instanceof Hashtable) {
					surfaceInfo = (Hashtable) value;
					dataType = SURFACE_INFO;
				}
				else {
					Logger.error("unknown surface data type??");
					return;
				}
			}
			mappingType = dataType;
			checkFlags();
			if (thePlane != null) {
				createIsosurface(); // for the plane
				initializeMesh(true);
				readVolumetricData(true); // for the data
				colorIsosurface();
			}
			else {
				readData(true);
				if (jvxlDataIsColorMapped) {
					jvxlReadColorData(currentMesh);
				}
				else {
					colorIsosurface();
				}
			}
			currentMesh.nBytes = nBytes;
			if (logMessages && !isSilent)
				Logger.debug("\n" + jvxlGetFile(currentMesh, jvxlFileMessage, true, 1));
			setModelIndex();
			discardTempData(true);
			dataType = SURFACE_NONE;
			return;
		}

		if ("delete" == propertyName) {
			if (currentMesh == null)
				nLCAO = 0;
			// fall through to meshCollection
		}

		// processed by meshCollection
		super.setProperty(propertyName, value, bs);
	}