/* $RCSfile$
* $Author: nicove $
* $Date: 2007-03-25 06:09:49 -0500 (Sun, 25 Mar 2007) $
* $Revision: 7221 $
*
* Copyright (C) 2000-2005 The Jmol Development Team
*
* Contact: jmol-developers@lists.sf.net
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.openscience.jvxl;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import javax.vecmath.Point4f;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.HelpFormatter;
import org.jmol.jvxl.readers.*;
import org.jmol.util.*;
public class Jvxl {
private final static String VERSION = "JVXL.java Version 1.0";
public static void main(String[] args) {
boolean blockData = false;
int fileIndex = Integer.MAX_VALUE;
String inputFile = null;
String mapFile = null;
String outputFile = null;
float cutoff = Float.NaN;
boolean isPositiveOnly = false;
String phase = null;
Point4f plane = null;
String colorScheme = null;
boolean bicolor = false;
boolean reverseColor = false;
float min = Float.NaN;
float max = Float.NaN;
Options options = new Options();
options.addOption("h", "help", false, "give this help page");
/*
* examples:
*
* jvxl ch3cl-density.cub --min=0.0 --max=0.2 --map ch3cl-esp.cub
* jvxl ethene-HOMO.cub --bicolor --output ethene.jvxl
* jvxl d_orbitals.jvxl --index 2 --phase yz
* jvxl d_orbitals.jvxl --map sets
* jvxl --plane xy --min=0.0 --max=0.2 --map data/ch3cl-density.cub
*/
// file options
options.addOption("B", "blockdata", false,
"multiple cube data are in blocks, not interspersed");
options.addOption("P", "progressive", false,
"create JVXL+ progressive X low-to-high format");
OptionBuilder.withLongOpt("file");
OptionBuilder.withDescription("file containing surface data");
OptionBuilder.withValueSeparator('=');
OptionBuilder.hasArg();
options.addOption(OptionBuilder.create("f"));
OptionBuilder.withLongOpt("index");
OptionBuilder.withDescription("index of surface in file (starting with 1)");
OptionBuilder.withValueSeparator('=');
OptionBuilder.hasArg();
options.addOption(OptionBuilder.create("i"));
OptionBuilder.withLongOpt("plane");
OptionBuilder
.withDescription("plane: x, y, z, xy, xz, yz, z2, x2-y2, or {a,b,c,d}");
OptionBuilder.withValueSeparator('=');
OptionBuilder.hasArg();
options.addOption(OptionBuilder.create("p"));
OptionBuilder.withLongOpt("map");
OptionBuilder
.withDescription("file containing data to map onto the surface or \"sets\"");
OptionBuilder.withValueSeparator('=');
OptionBuilder.hasArg();
options.addOption(OptionBuilder.create("m"));
OptionBuilder.withLongOpt("output");
OptionBuilder.withDescription("JVXL output file");
OptionBuilder.withValueSeparator('=');
OptionBuilder.hasArg();
options.addOption(OptionBuilder.create("o"));
// surface options
OptionBuilder.withLongOpt("cutoff");
OptionBuilder.withDescription("isosurface cutoff value");
OptionBuilder.withValueSeparator('=');
OptionBuilder.hasArg();
options.addOption(OptionBuilder.create("c"));
// color mapping options
options.addOption("b", "bicolor", false, "bicolor map (orbital)");
options.addOption("r", "reversecolor", false, "reverse color");
OptionBuilder.withLongOpt("colorScheme");
OptionBuilder
.withDescription("VRML color scheme: roygb, bgyor, rwb, bwr, low, high");
OptionBuilder.withValueSeparator('=');
OptionBuilder.hasArg();
options.addOption(OptionBuilder.create("s"));
OptionBuilder.withLongOpt("phase");
OptionBuilder
.withDescription("color by phase: x, y, z, xy, xz, yz, z2, x2-y2");
OptionBuilder.withValueSeparator('=');
OptionBuilder.hasArg();
options.addOption(OptionBuilder.create("F"));
OptionBuilder.withLongOpt("min");
OptionBuilder.withDescription("color absolute minimum value");
OptionBuilder.withValueSeparator('=');
OptionBuilder.hasArg();
options.addOption(OptionBuilder.create("n"));
OptionBuilder.withLongOpt("max");
OptionBuilder.withDescription("color absolute maximum value");
OptionBuilder.withValueSeparator('=');
OptionBuilder.hasArg();
options.addOption(OptionBuilder.create("x"));
CommandLine line = null;
try {
CommandLineParser parser = new PosixParser();
line = parser.parse(options, args);
} catch (ParseException exception) {
Logger.error("Unexpected exception: " + exception.toString());
}
if (line.hasOption("h")) {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("Jvxl", options);
return;
}
args = line.getArgs();
if (args.length > 0) {
inputFile = args[0];
}
// files
blockData = (line.hasOption("B"));
if (line.hasOption("i")) {
fileIndex = Parser.parseInt(line.getOptionValue("i"));
}
if (line.hasOption("f")) {
inputFile = line.getOptionValue("f");
}
if (line.hasOption("m")) {
mapFile = line.getOptionValue("m");
}
if (line.hasOption("p")) {
plane = getPlane(line.getOptionValue("p"));
if (plane == null) {
Logger.error("invalid plane");
return;
}
Logger.info("using plane " + plane);
if (mapFile == null)
mapFile = inputFile;
if (inputFile == null)
inputFile = mapFile;
}
if (line.hasOption("o")) {
outputFile = line.getOptionValue("o");
} else {
outputFile = inputFile;
if (outputFile.indexOf(".") < 0)
outputFile += ".";
String sIndex = (fileIndex == Integer.MAX_VALUE ? "" : "_" + fileIndex);
if (sIndex.length() == 0 && outputFile.indexOf(".jvxl") >= 0)
sIndex += "_new";
outputFile = outputFile.substring(0, outputFile.lastIndexOf("."))
+ sIndex + ".jvxl";
}
// Process more command line arguments
// these are also passed to viewer
bicolor = (line.hasOption("b"));
reverseColor = (line.hasOption("r"));
if (bicolor && mapFile != null) {
Logger.warn("--map option ignored; incompatible with --bicolor");
mapFile = null;
}
if (line.hasOption("c")) {
String s = line.getOptionValue("c");
if (s.indexOf("+") == 0) {
isPositiveOnly = true;
s = s.substring(1);
}
cutoff = Parser.parseFloat(s);
}
if (line.hasOption("n")) {
if (bicolor)
Logger.warn("--min option ignored; incompatible with --bicolor");
else
min = Parser.parseFloat(line.getOptionValue("n"));
}
if (line.hasOption("x")) {
if (bicolor)
Logger.warn("--max option ignored; incompatible with --bicolor");
else
max = Parser.parseFloat(line.getOptionValue("x"));
}
// if (line.hasOption("P")) {
// phase = line.getOptionValue("P");
// }
boolean progressive = line.hasOption("P");
// compose the surface
SurfaceGenerator sg = new SurfaceGenerator();
// input file
sg.setVersion(VERSION);
if (blockData)
sg.setParameter("blockData", Boolean.TRUE);
if (!Float.isNaN(cutoff))
sg.setParameter(isPositiveOnly ? "cutoffPositive" : "cutoff", new Float(
cutoff));
if (bicolor)
sg.setParameter("sign", null);
if (reverseColor)
sg.setParameter("reverseColor", null);
if (phase != null)
sg.setParameter("phase", phase);
if (progressive)
sg.setParameter("progressive", null);
if (plane != null)
sg.setParameter("plane", plane);
else {
if (fileIndex != Integer.MAX_VALUE)
sg.setParameter("fileIndex", new Integer(fileIndex));
Object t = FileReader
.getBufferedReaderOrErrorMessageFromName(inputFile);
if (t instanceof String) {
Logger.error((String) t);
return;
}
BufferedReader br = (BufferedReader) t;
sg.setParameter("readFile", br);
try {
br.close();
} catch (Exception e) {
//
}
}
sg.setParameter("title", line.toString());
//color scheme is only for VMRL
if (colorScheme != null)
sg.setParameter("colorScheme", new Object[] { colorScheme, Boolean.FALSE } );
if (!Float.isNaN(min))
sg.setParameter("red", new Float(min));
if (!Float.isNaN(max))
sg.setParameter("blue", new Float(max));
if (mapFile != null) {
Object t = FileReader
.getBufferedReaderOrErrorMessageFromName(mapFile);
if (t instanceof String) {
Logger.error((String) t);
return;
}
BufferedReader br2 = (BufferedReader) t;
sg.setParameter("mapColor", br2);
try {
br2.close();
} catch (Exception e) {
//
}
}
writeFile(outputFile, (String) sg.getProperty("jvxlFileData", 0));
Logger.info((String) sg.getProperty("jvxlFileInfo", 0));
Logger.info("\ncreated " + outputFile);
}
static void writeFile(String fileName, String text) {
try {
FileOutputStream os = new FileOutputStream(fileName);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os), 8192);
bw.write(text);
bw.close();
os = null;
} catch (IOException e) {
Logger.error("IO Exception: " + e.toString());
}
}
static Point4f getPlane(String str) {
if (str.equalsIgnoreCase("xy"))
return new Point4f(0, 0, 1, 0);
if (str.equalsIgnoreCase("xz"))
return new Point4f(0, 1, 0, 0);
if (str.equalsIgnoreCase("yz"))
return new Point4f(1, 0, 0, 0);
if (str.indexOf("x=") == 0) {
return new Point4f(1, 0, 0, -Parser.parseFloat(str.substring(2)));
}
if (str.indexOf("y=") == 0) {
return new Point4f(0, 1, 0, -Parser.parseFloat(str.substring(2)));
}
if (str.indexOf("z=") == 0) {
return new Point4f(0, 0, 1, -Parser.parseFloat(str.substring(2)));
}
if (str.indexOf("{") == 0) {
str = str.replace(',', ' ');
int[] next = new int[1];
return new Point4f(Parser.parseFloat(str, next), Parser.parseFloat(str,
next), Parser.parseFloat(str, next), Parser.parseFloat(str, next));
}
return null;
}
}
class FileReader {
FileReader() {
}
static Object getBufferedReaderOrErrorMessageFromName(String name) {
Object t = getInputStreamOrErrorMessageFromName(name);
if (t instanceof String)
return t;
try {
BufferedInputStream bis = new BufferedInputStream((InputStream)t, 8192);
InputStream is = bis;
return new BufferedReader(new InputStreamReader(is));
} catch (Exception ioe) {
return ioe.getMessage();
}
}
private static Object getInputStreamOrErrorMessageFromName(String name) {
String errorMessage = null;
try {
Logger.info("opening " + name);
File file = new File(name);
int length = (int) file.length();
InputStream in = new FileInputStream(file);
return new MonitorInputStream(in, length);
} catch (Exception e) {
errorMessage = "" + e;
}
return errorMessage;
}
}
class MonitorInputStream extends FilterInputStream {
int length;
int position;
int markPosition;
int readEventCount;
long timeBegin;
MonitorInputStream(InputStream in, int length) {
super(in);
this.length = length;
this.position = 0;
timeBegin = System.currentTimeMillis();
}
public int read() throws IOException{
++readEventCount;
int nextByte = super.read();
if (nextByte >= 0)
++position;
return nextByte;
}
public int read(byte[] b) throws IOException {
++readEventCount;
int cb = super.read(b);
if (cb > 0)
position += cb;
return cb;
}
public int read(byte[] b, int off, int len) throws IOException {
++readEventCount;
int cb = super.read(b, off, len);
if (cb > 0)
position += cb;
return cb;
}
public long skip(long n) throws IOException {
long cb = super.skip(n);
// this will only work in relatively small files ... 2Gb
position = (int)(position + cb);
return cb;
}
public void mark(int readlimit) {
super.mark(readlimit);
markPosition = position;
}
public void reset() throws IOException {
position = markPosition;
super.reset();
}
int getPosition() {
return position;
}
int getLength() {
return length;
}
int getPercentageRead() {
return position * 100 / length;
}
int getReadingTimeMillis() {
return (int)(System.currentTimeMillis() - timeBegin);
}
}