/*
* This file is part of MoleculeViewer.
*
* MoleculeViewer 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 3 of the License, or
* (at your option) any later version.
*
* MoleculeViewer 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 MoleculeViewer. If not, see <http://www.gnu.org/licenses/>.
*/
package astex;
/* Copyright Astex Technology Ltd. 1999 */
/* Copyright David Hall, Boston University, 2011 */
/*
* 20-10-03 mjh
* Fix labelling of symmetry generated atoms. They now have the
* correct residue and chain name from the original molecule.
* The atom numbering problem still exists.
* 15-08-02 mjh
* Completely rejig the drawing of CA traces and normal
* bond styles. For some reason they were mutually
* exclusive, now you can have the trace displayed
* as well as atoms which lets you make simplified
* schematic displays. The traces are always drawn wide now.
* 07-08-02 mjh
* Remove the restriction on 255 atoms for drawing bond orders.
* Also make aromatic bonds in structures get drawn with
* dotted lines as you would expect.
*
* 06-08-02 mjh
* XXX Need to fix the atom renumbering that goes on when
* symmetry is generated. This breaks all sorts of atom
* selections.
*
* 15-03-02 mjh
* Fix bug in symmetry generation when molecule was not in
* primary unit cell.
* 15-07-01 mjh
* Fix bug in atom picking that meant that atoms in undisplayed
* molecules got picked. Caused by not checking the molecule display
* status before checking the atom display status.
* 01-12-00 mjh
* Add simple grid type, so that superstar like grids
* can be contoured.
* 17-03-00 mjh
* fix a bug in the map contouring that caused the center grid
* point to be miscalculated. Related to the bug that caused the
* wrong region to be contoured in the contouring code.
* 23-12-99 mjh
* merge MoleculeScene back into this file as it was too confusing
* when the functionality was split between the two values. make
* setCenter set to the map radius even if there are no maps
* being displayed.
* Pick up the properties from a file rather than being hard wired.
* 07-12-99 mjh
* fix serious bug in initialiseCenter which meant
* that the center wasn't correctly calculated as MIN_VALUE
* is the smallest number not the most negative
* 05-12-99 mjh
* switch over to using a moleculeScene to keep track of
* all the objects we want to render.
* 15-11-99 mjh
* created
*/
import java.awt.Color;
import java.util.*;
import java.io.*;
import java.net.*;
import java.lang.reflect.*;
import astex.parser.*;
import astex.generic.*;
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
import it.unimi.dsi.fastutil.floats.FloatArrayList;
import it.unimi.dsi.fastutil.ints.IntArrayList;
/**
* Class for drawing a molecule into our renderer.
*/
//public class MoleculeRenderer extends Renderer implements Function {
public class MoleculeRenderer {
/** The Renderer that will do the work. */
public Renderer renderer = new Renderer();
/** The moleucle viewer class that we are drawing for. */
public transient MoleculeViewer moleculeViewer = null;
/** The list of molecules. */
private List<Molecule> molecules = new ArrayList<Molecule>(10);
/** The list of maps. */
private List<Map> maps = new ArrayList<Map>(10);
/** The list of selected atoms. */
private List<Atom> selectedAtoms = new ArrayList<Atom>(100);
/** The list of atoms that are bumped. */
private List<Atom> bumpAtoms = new ArrayList<Atom>(100);
/** The list of atoms that are showing distance monitors. */
private List<Distance> distances = new ArrayList<Distance>(10);
/** The list of atoms that are showing torsion monitors. */
private List<Atom> torsions = new ArrayList<Atom>(20);
/** The list of hydrogen bonds. */
private List<Bond> hbonds = new ArrayList<Bond>(100);
/** The list of MoleculeRendererListeners. */
private transient List<MoleculeRendererListener> moleculeRendererListeners =
new ArrayList<MoleculeRendererListener>(5);
/** The set of defined groups. */
public HashMap<String,HashSet<Atom>> groups = new HashMap<String,HashSet<Atom>>(11);
/** The symmetry we will use. */
private Symmetry symmetry = null;
/** The radius of map that we will display. */
private double mapRadius = 7.0;
/** The map radius property name. */
private static String mapRadiusProperty = "map.radius";
/** The radius of symmetry generated atoms that we will generate. */
//private double symmetryRadius = 12.0;
private double symmetryRadius = 15.0;
/** The symmetry radius property name. */
private static String symmetryRadiusProperty = "symmetry.radius";
/** The radius that we will display when we set origin. */
private double displayRadius = 6.0;
/** The minimum radius that we will allow to set. */
private static final double minimumDisplayRadius = 6.0;
/** The minimum clip distance we will allow. */
private double minimumClipDistance = 6.0;
/** The view radius property name. */
private static String displayRadiusProperty = "display.radius";
/** The contour level property name. */
private static String contourLevelsProperty = "contour.levels";
/** Should we display symmetry at all. */
private boolean displaySymmetry = true;
/** Should we display atom bumps. */
private boolean displayBumps = false;
/** Should bumps be restrictd to atoms in the same molecule. */
private boolean bumpInSameMolecule = false;
/** Should we display atom distances. */
private boolean displayDistances = true;
/** Should we display electron density maps. */
private boolean displayMaps = true;
/** Should we display solvent. */
private boolean displaySolvent = true;
public enum PickMode {
NORMAL_PICK,DISTANCE_PICK,ANGLE_PICK,TORSION_PICK
}
/** The current pick mode. */
public PickMode pickMode = PickMode.NORMAL_PICK;
public boolean allowFastDraw = true;
private boolean fastDraw = false;
private boolean selectCount = true;
/** Are we debugging. */
private final boolean debug = false;
/**
* The current contour grid size.
* The number of grid points we contour around
* the map center.
*/
private int contourSize = 24;
public boolean hersheyFonts = false;
/** Default constructor. */
public MoleculeRenderer(){
super();
// set up the default contour levels
resetContourLevels();
addContourLevel();
addContourLevel();
addContourLevel();
hersheyFonts = Settings.getBoolean("fonts", "hershey.fonts");
shadows = Settings.getBoolean("config", "shadows");
bondLineRadius = Settings.getDouble("config", "bondlineradius");
initialise();
}
public void repaint(){
if(moleculeViewer != null){
moleculeViewer.dirtyRepaint();
}
}
public void setContourSize(int cs){
contourSize = cs;
}
/** Set the picking mode. */
public void setPickMode(PickMode pick){
if(pick != PickMode.NORMAL_PICK &&
pick != PickMode.DISTANCE_PICK &&
pick != PickMode.ANGLE_PICK &&
pick != PickMode.TORSION_PICK){
pick = PickMode.NORMAL_PICK;
}
pickMode = pick;
}
/** Set whether or not we print select counts. */
public void setSelectCount(boolean b){
selectCount = b;
}
/** Return whether we are printing select counts. */
public boolean getSelectCount(){
return selectCount;
}
/** Handle a delete atoms command. */
public void handleDeleteCommand(List<Atom> selectedAtoms){
for(Atom atom : selectedAtoms){
deleteAtom(atom);
}
}
/** Handle an atom edit command. */
public void handleEditCommand(String name, String value, List<Atom> selectedAtoms){
for(Atom atom : selectedAtoms){
atom.edit(name, value);
}
}
/** Actually delete an atom. */
private void deleteAtom(Atom atom){
int bondCount = atom.getBondCount();
Molecule mol = atom.getMolecule();
for(int b = bondCount - 1; b >= 0; b--){
Bond bond = atom.getBond(b);
Atom other = bond.getOtherAtom(atom);
atom.removeBond(bond);
other.removeBond(bond);
mol.removeBond(bond);
}
mol.removeAtom(atom);
}
/** The list of atoms that have been picked so far. */
public List<Atom> pickedAtoms = new ArrayList<Atom>(4);
/**
* Handle a pick on the screen and
* label the appropriate item.
*/
public void handlePick(Atom pickedAtom){
if(pickMode == PickMode.NORMAL_PICK){
return;
}
// add the latest atom to the pick list
if(pickMode == PickMode.DISTANCE_PICK){
if(pickedAtoms.size() == 2){
Atom atom0 = pickedAtoms.get(0);
Atom atom1 = pickedAtoms.get(1);
Distance distance =Distance.createDistanceMonitor(atom0, atom1);
addDistance(distance);
pickedAtoms.clear();
}
}else if(pickMode == PickMode.ANGLE_PICK){
if(pickedAtoms.size() == 3){
Atom atom0 = pickedAtoms.get(0);
Atom atom1 = pickedAtoms.get(1);
Atom atom2 = pickedAtoms.get(2);
addAngle(atom0, atom1, atom2);
pickedAtoms.clear();
}
}else if(pickMode == PickMode.TORSION_PICK){
if(pickedAtoms.size() == 4){
Atom atom0 = pickedAtoms.get(0);
Atom atom1 = pickedAtoms.get(1);
Atom atom2 = pickedAtoms.get(2);
Atom atom3 = pickedAtoms.get(3);
addTorsion(atom0, atom1, atom2, atom3);
pickedAtoms.clear();
}
}else{
System.out.println("Invalid pick mode");
}
}
/** Handle a command that updates the properties of atoms. */
public void handleUpdateCommand(Arguments args, FloatArrayList fa){
}
/** Interpret a new style map command. */
public void handleMapCommand(String name, Arguments args){
List<Map> maps = getMaps(name);
for(Map map : maps){
if(args.defined("-volumerender") || args.defined("-volumecolor") ||
args.defined("-volumemin") || args.defined("-volumemax")){
if(args.defined("-volumerender")){
map.volumeRender = args.getBoolean("-volumerender", false);
}
if(args.defined("-volumecolor")){
map.volumeColor = args.getColor("-volumecolor", Color32.red);
}
if(args.defined("-volumemin")){
map.volumeMin = args.getDouble("-volumemin", 0.0);
}
if(args.defined("-volumemax")){
map.volumeMax = args.getDouble("-volumemax", 1.0);
}
}else{
System.out.println("changing " + map.getName());
boolean changed = false;
double centerValue = args.getDouble("-multicenter",
Double.NEGATIVE_INFINITY);
if(centerValue != Double.NEGATIVE_INFINITY){
double spread = args.getDouble("-multispread", 1.0);
for(int j = 0; j < Map.MaximumContourLevels; j++){
map.setContourLevel(j, centerValue);
centerValue += spread;
}
changed = true;
}
String contourColourString = args.getString("-multicolor", null);
if(contourColourString != null){
int colors[] = new int[3];
int centerColour =
Color32.getColorFromName(contourColourString);
colors[0] = centerColour;
colors[1] = Color32.add(centerColour, Color32.scale(Color32.white, 64));
colors[2] = Color32.add(centerColour, Color32.scale(Color32.white, 128));
for(int j = 0; j < Map.MaximumContourLevels; j++){
map.setContourColor(j, colors[j]);
}
changed = true;
}
if(changed){
for(int j = 0; j < Map.MaximumContourLevels; j++){
contourMap(map, j);
}
}
}
}
}
/**
* Handle a user command.
* These are of the form
* user -class classname [args];
* The method will load the class and then pass the
* argument object to it and the moleculeViewer instance.
*/
public void handleUserCommand(String command, Arguments args) throws Throwable{
String className = command;
if(className == null){
Log.error("command name is null");
return;
}
try {
// load the class and try and create one.
String userMethodName = args.getString("-method", "handleCommand");
Class<?> userClass = Class.forName(className);
Object userObject = userClass.newInstance();
Class<?> userMethodParams[] = {
MoleculeViewer.class,
MoleculeRenderer.class,
Arguments.class
};
Object userParams[] = { moleculeViewer, this, args };
Method userMethod =
userClass.getDeclaredMethod(userMethodName, userMethodParams);
try {
userMethod.invoke(userObject, userParams);
}catch(InvocationTargetException e){
// throw e.getCause();
Exception realException = (Exception)e.getTargetException();
realException.printStackTrace();
throw realException;
}
moleculeViewer.dirtyRepaint();
}catch(Exception e){
Log.error("error loading user class " + className);
e.printStackTrace();
}
}
/**
* Interpret a molecule write command.
*
* write
* -file 'filename'
* -url 'urlname'
* -molecule 'molecule_name'
* ;
*/
public void handleWriteCommand(Arguments args){
String typeString = args.getString("-type", null);
String urlString = args.getString("-url", null);
String fileString = args.getString("-file", null);
String moleculeString = args.getString("-molecule", null);
String parameterName = args.getString("-parameter", "pdb");
boolean selected = args.getBoolean("-selected", true);
Molecule mol = null;
String type = null;
if(moleculeString != null){
mol = getMolecule(moleculeString);
if(mol == null){
Log.error("no molecule " + moleculeString);
}
}else if(selected){
// get molecule from selected atoms
// just takes first one it finds
AtomIterator iterator = getAtomIterator();
while(iterator.hasMoreElements()){
Atom atom = iterator.getNextAtom();
if(atom.isSelected()){
mol = atom.getMolecule();
break;
}
}
}else{
Log.error("no molecule specification specified");
return;
}
if(mol == null){
Log.error("no molecule matches specification");
return;
}
if(typeString != null && typeString.charAt(0) != '.'){
typeString = "." + typeString;
}
type = MoleculeIO.getTypeFromExtension(typeString);
if(urlString != null){
writeMoleculeToUrl(mol, urlString, type, parameterName);
}else if(fileString != null){
writeMoleculeToFile(mol, fileString, type);
}else{
Log.error("you must specify -file or -url");
}
}
/**
* Write the molecule to the specified file.
*/
private void writeMoleculeToFile(Molecule mol, String fileString, String type){
FILE file = FILE.write(fileString);
if(file == null){
Log.error("couldn't open " + fileString);
return;
}
MoleculeIO.write(mol, file, type);
file.close();
}
/**
* Write the molecule to the url.
*
* This uses the POST method to communicate with the server.
* The additional parameters are stripped off and sent
* along with the pdb file, which is appended as
* the pdb=.... parameter.
*
* This uses the POST implementation described in
* 'Dodge the traps hiding in the URLConnection class'
* http://www.javaworld.com/javaworld/jw-03-2001/jw-0323-traps.html
*
* I would suggest not changing anything to do with the
* URLConnection methods unless you know much more about
* this than I do.
*/
private String writeMoleculeToUrl(Molecule mol, String urlString,
String type, String parameterName){
StringBuilder result = new StringBuilder(16);
try {
URL url = new URL(urlString);
URLConnection con = url.openConnection();
con.setDoInput(true);
con.setDoOutput(true);
con.setUseCaches(false);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
FILE file = new FILE((OutputStream)baos);
MoleculeIO.write(mol, file, type);
int questionPos = urlString.indexOf('?');
String parameterSection = null;
if(questionPos != -1){
parameterSection = urlString.substring(questionPos + 1,
urlString.length());
}
StringBuilder msg = new StringBuilder(16);
if(parameterSection != null){
msg.append(parameterSection).append("&");
}
msg.append(parameterName).append("=").append(baos);
con.setRequestProperty("CONTENT_LENGTH", Integer.toString(msg.length()));
OutputStream os = con.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os);
osw.write(msg.toString());
osw.flush();
osw.close();
InputStream is = con.getInputStream();
// any response?
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ( (line = br.readLine()) != null) {
if(!line.startsWith("OK")){
System.out.println(line);
}
result.append(line);
}
br.close();
} catch (Throwable t) {
t.printStackTrace();
}
return result.toString();
}
/** Handle a distance command. */
public void handleDistanceCommand(Arguments args){
List<Atom> from = (List<Atom>)args.get("-from");
List<Atom> to = (List<Atom>)args.get("-to");
String mode = args.getString("-mode", "pairs");
double dmax = args.getDouble("-dmax", 5.0);
double contact = args.getDouble("-contact", 0.5);
String visible = args.getString("-visible", null);
if(visible != null){
String pattern = args.getString("-name", null);
for(Distance d : distances){
String name = d.getString(Generic.Name, null);
if(pattern == null || (name != null && match.matches(pattern, name))){
d.setBoolean(Distance.Visible, "on".equals(visible));
}
}
}else if(from != null && "bumps".equals(mode)){
Distance d = new Distance();
configureDistance(d, args);
d.setInteger(Distance.Mode, Distance.Pairs);
addDistance(d);
int fromCount = from.size();
for(int i = 0; i < fromCount; i++){
Atom froma = from.get(i);
double fromr = froma.getVDWRadius();
for(int j = i + 1; j < fromCount; j++){
Atom toa = from.get(j);
double dist = froma.distance(toa);
if(dist < dmax && froma != toa &&
dist < (toa.getVDWRadius() + fromr + contact) &&
!froma.connected121314(toa)){
d.group0.add(froma);
d.group1.add(toa);
}
}
}
}else if(from != null && to != null){
System.out.println("fromCount "+ from.size());
System.out.println("toCount "+ to.size());
// create a distance object
if("centroid".equals(mode)){
Distance d = new Distance();
d.setInteger(Distance.Mode, Distance.Centroids);
for(Atom a : from){
d.group0.add(a);
}
for(Atom a : to){
d.group1.add(a);
}
if(d.getCenter0().distance(d.getCenter1()) < dmax){
configureDistance(d, args);
addDistance(d);
}
}else if("pairs".equals(mode) || "nbpairs".equals(mode)){
boolean allowBonded = true;
if("nbpairs".equals(mode)){
allowBonded = false;
}
Distance d = new Distance();
configureDistance(d, args);
d.setInteger(Distance.Mode, Distance.Pairs);
addDistance(d);
for(Atom froma : from){
double fromr = froma.getVDWRadius();
for(Atom toa : to){
double dist = froma.distance(toa);
if(dist < dmax && froma != toa &&
dist < (toa.getVDWRadius() + fromr + contact) &&
allowBonded || !froma.connected121314(toa)){
d.group0.add(froma);
d.group1.add(toa);
}
}
}
}
}else{
String deletePattern = args.getString("-delete", null);
if(deletePattern != null){
deleteDistances(deletePattern);
}else{
System.out.println("distance: you must specify "+
"-from and -to or -delete");
}
}
}
/** Delete distances that match the pattern. */
private void deleteDistances(String pattern){
int deleted = 0;
for(Iterator<Distance> it = distances.iterator(); it.hasNext(); ){
Distance d = it.next();
String name = d.getString(Generic.Name, null);
if(name != null && match.matches(pattern, name)){
fireGenericRemovedEvent((Generic)d);
deleted++;
it.remove();
}
}
System.out.println("deleted " + deleted + " distances");
}
/** Configure the settings of a distance object. */
private void configureDistance(Distance d, Arguments args){
d.setBoolean(Distance.Visible, true);
d.setDouble(Distance.Radius, args.getDouble("-radius", 0.0));
d.setDouble(Distance.On, args.getDouble("-on", 0.0));
d.setDouble(Distance.Off, args.getDouble("-off", 0.0));
d.setString(Distance.Format, args.getString("-format", null));
d.setString(Generic.Name, args.getString("-name", null));
String colour = args.getString("-colour", "white");
d.set(Distance.Color, new Color(Color32.getColorFromName(colour)));
colour = args.getString("-labelcolour", "white");
d.set(Distance.LabelColor, new Color(Color32.getColorFromName(colour)));
}
/**
* Handle a hydrogen bond command.
*/
public void handleHbondCommand(Arguments args){
List<Atom> calculateAtoms = (List<Atom>)args.get("-calculate");
List<Atom> deleteAtoms = (List<Atom>)args.get("-delete");
boolean deleteAll = args.getBoolean("-deleteall", false);
if(deleteAll){
hbonds.clear();
}
if(calculateAtoms != null){
calculateHbonds(calculateAtoms, args);
}else if(deleteAtoms != null){
deleteHbonds(deleteAtoms);
}
}
/** Calculate hydrogen bonds. */
private void calculateHbonds(List<Atom> atoms, Arguments args){
Lattice l = new Lattice(5.5);
double hbondConstant = args.getDouble("hbond.constant", -999.0);
double hbondCutoff = args.getDouble("hbond.cutoff", -999.0);
System.out.println("hbondCutoff " + hbondCutoff);
for(ListIterator<Atom> it = atoms.listIterator(); it.hasNext();){
int i = it.nextIndex();
Atom a = it.next();
if("O".equals(a.getAtomLabel())){
l.add(i, a.x, a.y, a.z);
}
}
// space for the neighbours
IntArrayList neighbours = new IntArrayList();
for(Atom a : atoms){
if("N".equals(a.getAtomLabel())){
Atom n = a;
Point3d h = getAmideHydrogen(a);
neighbours.clear();
l.getPossibleNeighbours(Lattice.Undefined,
n.x, n.y, n.z,
neighbours, true);
int neighbourCount = neighbours.size();
for(int j = 0; j < neighbourCount; j++){
Atom o = atoms.get(neighbours.getInt(j));
Atom c = o.getBondedAtom("C");
double e = hbondEnergy(n, h, o, c, hbondConstant);
if(e < hbondCutoff){
Bond hbond = new Bond(n, o);
hbonds.add(hbond);
}
}
}
}
System.out.println("number of hbonds " + hbonds.size());
}
/** Delete hbonds that involve atoms in the selection. */
private void deleteHbonds(List<Atom> atoms){
int removedCount = 0;
for(Iterator<Bond> it = hbonds.iterator(); it.hasNext(); ){
Bond hbond = it.next();
Atom firstAtom = hbond.getFirstAtom();
Atom secondAtom = hbond.getSecondAtom();
if(atoms.contains(firstAtom) || atoms.contains(secondAtom)){
it.remove();
removedCount++;
}
}
FILE.out.print("removed %d hydrogen bonds", removedCount);
}
/**
* Calculate the energy of an hbond as defined in
*
* Kabsch and Sander, 'Dictionary of Protein Secondary
* Structure: Pattern Recognition of Hydrogen-Bonded and
* Geometrical Features', Biopolymers, 22, 2577-2637 (1983).
*/
public static double hbondEnergy(Atom n, Point3d h,
Atom o, Atom c,
double fq1q2){
if((n != null && h != null &&
o != null && c != null) &&
(!c.hasBond(n) || !c.hasBond(o))){
double rON = o.distance(n);
double rCH = c.distance(h);
double rOH = o.distance(h);
double rCN = c.distance(n);
Point3d nh = Point3d.unitVector(n, h);
Point3d no = Point3d.unitVector(n, o);
Point3d co = Point3d.unitVector(c, o);
double hno = (180./Math.PI)* Math.acos(nh.dot(no));
double hnoc = (180./Math.PI)* Math.acos(nh.dot(co));
if(hno > 63.0 || hnoc < 90.0){
return 10000.0;
}
return fq1q2 * (1./rON + 1./rCH - 1./rOH - 1./rCN);
}
return 10000.0;
}
/** Calculate the hdyrogen position for this nitrogen. */
private static Point3d getAmideHydrogen(Atom N){
if(N == null){
return null;
}
Atom CA = N.getBondedAtom("CA");
Atom C = N.getBondedAtom("C");
if(N == null || CA == null || C == null){
return null;
}
Point3d hpos = new Point3d();
hpos.set(N);
hpos.sub(C);
hpos.add(N);
hpos.sub(CA);
hpos.normalize();
hpos.scale(1.04);
hpos.add(N);
Molecule m = N.getMolecule();
//
Atom ah = m.addAtom();
ah.set(hpos);
return hpos;
}
/**
* Interpret a texture command.
*/
public void handleTextureCommand(Arguments args){
String name = args.getString("-name", null);
Texture t = null;
if(name != null){
t = renderer.textures.get(name);
if(t == null){
t = new Texture();
renderer.textures.put(name, t);
System.out.println("adding new texture " + name);
}
}else{
System.out.println("handleTextureCommand: no texture specified [-name]");
return;
}
String coord = args.getString("-coord", null);
int tc = 0;
if(coord != null){
if("u".equals(coord)){
tc = 0;
}else if("v".equals(coord)){
tc = 1;
}else{
tc = -1;
}
}
if(tc == -1){
System.out.println("handleTextureCommand: illegal texture coord " + coord);
return;
}
String values = args.getString("-values", null);
if(values != null){
String colors[] = FILE.split(values);
t.fillValues(colors, tc);
}
}
/** Handle a light command. */
public void handleLightCommand(int l, Arguments args){
if(l < 0 || l >= renderer.lights.size()){
return;
}
Light light = renderer.lights.get(l);
boolean changed = false;
if(light == null){
return;
}
if(args.defined("-x")){
light.pos[0] = args.getDouble("-x", 0.0);
changed = true;
}
if(args.defined("-y")){
light.pos[1] = args.getDouble("-y", 0.0);
changed = true;
}
if(args.defined("-z")){
light.pos[2] = args.getDouble("-z", 1.0);
changed = true;
}
if(args.defined("-on")){
light.on = args.getBoolean("-on", true);
changed = true;
}
if(args.defined("-specularint")){
int intensity = args.getInteger("-specularint", 64);
light.specular = Color32.pack(intensity, intensity, intensity);
changed = true;
}
if(args.defined("-phongpower")){
double intensity = args.getDouble("-phongpower", 16.0);
light.power = intensity;
changed = true;
}
if(args.defined("-diffuseint")){
int intensity = args.getInteger("-diffuseint", 128);
light.diffuse = Color32.pack(intensity, intensity, intensity);
changed = true;
}
if(changed){
light.normalisePos();
renderer.calculateLightMap();
moleculeViewer.dirtyRepaint();
}
}
/** Handle an object command. */
public void handleObjectCommand(String namePattern, Arguments args){
List<Tmesh> objects = renderer.getGraphicalObjects(namePattern);
for(Tmesh tmesh : objects){
if(args.defined("-map")){
List<Atom> mapAtoms = (List<Atom>)args.get("-map");
// back ground color
int defaultColor = tmesh.getColor();
if(args.defined("-defaultcolor")){
String defaultColorName = args.getString("-defaultcolor", "white");
defaultColor = Color32.getColorFromName(defaultColorName);
}
// maximum distance before we become default color
double dmax = args.getDouble("-dmax", 1.5);
double wmax = args.getDouble("-wmax", 1000.0);
// wmax is used as 1/(d + wmax)
wmax = 1./wmax;
mapAtomColors(tmesh, mapAtoms, defaultColor, dmax, wmax);
if(tmesh.lines != null){
mapAtomColors(tmesh.lines, mapAtoms, defaultColor, dmax, wmax);
}
if(tmesh.spheres != null){
mapAtomColors(tmesh.spheres, mapAtoms, defaultColor, dmax, wmax);
}
if(tmesh.cylinders != null){
mapAtomColors(tmesh.cylinders, mapAtoms, defaultColor, dmax, wmax);
}
}
}
}
/** Map the colors of a set of atoms to the object. */
private void mapAtomColors(Tmesh tmesh, List<Atom> mapAtoms,
int defaultColor, double dmax, double wmax){
double maxRadius = 0.0;
for(Atom atom : mapAtoms){
double r = atom.getVDWRadius();
if(r > maxRadius){
maxRadius = r;
}
}
maxRadius += dmax;
// build the lattice structure for atom lookup
Lattice l = new Lattice(maxRadius + 0.01);
for(ListIterator<Atom> it = mapAtoms.listIterator(); it.hasNext();){
int a = it.nextIndex();
Atom atom = it.next();
l.add(a, atom.x, atom.y, atom.z);
}
// find the nearest atom for each tmesh point
int pointCount = tmesh.np;
IntArrayList neighbours = new IntArrayList();
IntArrayList nearest = new IntArrayList();
DoubleArrayList distances = new DoubleArrayList();
for(int i = 0; i < pointCount; i++){
neighbours.clear();
double tx = tmesh.x[i];
double ty = tmesh.y[i];
double tz = tmesh.z[i];
l.getPossibleNeighbours(-1, tx, ty, tz, neighbours, true);
int neighbourCount = neighbours.size();
double dnear = 1.e10;
nearest.clear();
distances.clear();
for(int j = 0; j < neighbourCount; j++){
int neighbour = neighbours.getInt(j);
Atom atom = mapAtoms.get(neighbour);
double ar = atom.getVDWRadius();
double dx = atom.x - tx;
double dy = atom.y - ty;
double dz = atom.z - tz;
double d = Math.sqrt(dx*dx + dy*dy + dz*dz);
if(d < ar + dmax){
nearest.add(neighbour);
d -= ar;
d += wmax;
d = 1. / Math.abs(d) - 1./dmax;
distances.add(d);
if(d < dnear){
dnear = d;
}
}
}
int nearCount = nearest.size();
if(nearCount == 0){
tmesh.vcolor[i] = defaultColor;
}else if(nearCount == 1){
int a = nearest.getInt(0);
Atom atom = mapAtoms.get(a);
int color = atom.getColor();
tmesh.vcolor[i] = color;
}else{
double sum = 0.0;
for(int nn = 0; nn < nearCount; nn++){
sum += distances.getDouble(nn);
}
int r = 0, g = 0, b = 0;
for(int nn = 0; nn < nearCount; nn++){
int a = nearest.getInt(nn);
Atom atom = mapAtoms.get(a);
int color = atom.getColor();
double comp = distances.getDouble(nn)/sum;
r += comp * Color32.getRed(color);
g += comp * Color32.getGreen(color);
b += comp * Color32.getBlue(color);
}
tmesh.vcolor[i] = Color32.getClampColor(r, g, b);
}
}
tmesh.setColorStyle(Tmesh.ColorStyle.VertexColor);
}
/** Name of properties resource for the MoleculeRenderer class. */
private static final String moleculeRendererProperties =
"MoleculeRenderer.properties";
/** The properties for the moleculerenderer. */
private Properties properties = null;
/** Initialise the MoleculeRenderer. */
private void initialise(){
properties =
FILE.loadProperties(moleculeRendererProperties);
String value = null;
value = properties.getProperty(displayRadiusProperty);
if(value != null){
displayRadius = FILE.readDouble(value);
}
value = properties.getProperty(symmetryRadiusProperty);
if(value != null){
symmetryRadius = FILE.readDouble(value);
}
value = properties.getProperty(mapRadiusProperty);
if(value != null){
mapRadius = FILE.readDouble(value);
}
value = properties.getProperty(contourLevelsProperty);
if(value != null){
setupContourLevels(value);
}
}
/** The default status string. */
private String defaultStatusLabelFormat = null;
/** Generate a label for the status bar. */
private String generateStatusLabel(Atom atom){
if(defaultStatusLabelFormat == null){
defaultStatusLabelFormat = Settings.getString("config", "atom.long.format");
if(defaultStatusLabelFormat == null){
// no config so set it here
defaultStatusLabelFormat = "%a %R %c:%r ID=%i X=%x Y=%y Z=%z O=%o B=%b %m";
}
}
return atom.generateLabel(defaultStatusLabelFormat);
}
/** Set the atom that will produce the status label. */
public void setStatusAtom(Atom a){
if(a == null){
renderer.setStatusString(null);
}else{
renderer.setStatusString(generateStatusLabel(a));
}
}
/** Set the radius allowing for minimum value for renderer. */
public void setRadius(double radius){
if(radius < minimumDisplayRadius){
radius = minimumDisplayRadius;
}
displayRadius = radius;
renderer.setRadius(radius);
}
/** Set the clip distance allowing for minimum clip distance. */
public void setClip(double distance){
if(distance < minimumClipDistance){
distance = minimumClipDistance;
}
renderer.setClip(distance);
}
private void removeGraphicalObjectsBeginningWith(String prefix){
renderer.removeGraphicalObjectsBeginningWith(prefix);
}
public void removeGraphicalObjects(String prefix){
renderer.removeGraphicalObjects(prefix);
}
public void addGraphicalObject(Tmesh object){
renderer.addTmesh(object);
}
/** Setup the contour levels from the passed string. */
private void setupContourLevels(String contourLevels){
resetContourLevels();
String levels[] = FILE.split(contourLevels, ",");
for(int i = 0; i < levels.length; i++){
String attributes[] = FILE.split(levels[i], ":");
addContourLevel();
}
}
/** Set whether we display symmetry. */
public void setSymmetry(boolean state){
displaySymmetry = state;
if(displaySymmetry){
generateSymmetry();
}else{
removeSymmetry();
}
}
/** Remove the stored space group information. */
private void removeSpaceGroup(){
symmetry = null;
}
/** Add a MoleculeRendererListener. */
public void addMoleculeRendererListener(MoleculeRendererListener l){
moleculeRendererListeners.add(l);
}
/** Remove a MoleculeRendererListener. */
public void removeMoleculeRendererListener(MoleculeRendererListener l){
moleculeRendererListeners.remove(l);
System.out.println("moleculeRendererListeners " + moleculeRendererListeners.size());
}
/** Fire a molecule added event. */
public void fireMoleculeAddedEvent(Molecule molecule){
for(MoleculeRendererListener l : moleculeRendererListeners){
l.moleculeAdded(this, molecule);
}
}
/** Fire a molecule removed event. */
public void fireMoleculeRemovedEvent(Molecule molecule){
for(MoleculeRendererListener l : moleculeRendererListeners){
l.moleculeRemoved(this, molecule);
}
}
/** Fire a distance added event. */
private void fireGenericAddedEvent(Generic generic){
for(MoleculeRendererListener l : moleculeRendererListeners){
l.genericAdded(this, generic);
}
}
/** Fire a generic removed event. */
private void fireGenericRemovedEvent(Generic generic){
for(MoleculeRendererListener l : moleculeRendererListeners){
l.genericRemoved(this, generic);
}
}
/** Fire a map added event. */
private void fireMapAddedEvent(Map map){
for(MoleculeRendererListener l : moleculeRendererListeners){
l.mapAdded(this, map);
}
}
/** Fire an atom selected event. */
private void fireAtomSelectedEvent(Atom atom){
for(MoleculeRendererListener l : moleculeRendererListeners){
l.atomSelected(this, atom);
}
}
/** Add a molecule to the scene. */
public void addMolecule(Molecule molecule){
molecules.add(molecule);
if(molecules.size() == 1){
resetView();
initialiseCenter();
}
fireMoleculeAddedEvent(molecule);
}
/** Add a molecule to the scene. */
public void addMolecule(String filename, String name){
boolean sdf = (filename.toLowerCase().indexOf(".sd") != -1);
// only implement this file loading semantics
// for applications, its too confusing in an applet.
if(!moleculeViewer.isApplication()){
sdf = false;
}
System.out.println("sdf " + sdf);
if(sdf){
FILE f = FILE.open(filename);
if(f == null){
System.out.println("addMolecule: couldn't load " + filename);
}
Molecule molecule = null;
int count = 0;
int start = getMoleculeCount();
while((molecule = MoleculeIO.readMDLMol(f)) != null){
String molname = molecule.getName();
if(molname == null || molname.length() == 0){
molname = filename + "_" + count;
molecule.setName(molname);
}
System.out.println("molecule " + count + " name " + molecule.getName());
molecule.setFilename(filename);
addMolecule(molecule);
count++;
}
int finish = getMoleculeCount();
StringBuilder sel = new StringBuilder("molecule");
for(int m = start; m < finish; m++){
sel.append(" '#").append(m).append("'");
}
StringBuilder command = new StringBuilder(100);
command.append("cylinder_radius 0.09 ").append(sel).append(";");
command.append("display cylinders on ").append(sel).append(";");
execute(command.toString());
if(finish - start == 1){
// added a single mol from the sd file
// rename it to be the name that was given
Molecule mol = getMolecule(start);
mol.setName(name);
System.out.println("renamed single molecule sd to " + name);
}
}else{
Molecule molecule = MoleculeIO.read(filename);
if(molecule != null){
molecule.setFilename(filename);
molecule.setName(name);
addMolecule(molecule);
}else{
System.out.println("addMolecule: couldn't load " + filename);
}
}
}
/** Return the total number of molecules. */
public int getMoleculeCount(){
return molecules.size();
}
/** Return the specified molecule. */
public Molecule getMolecule(int i){
return molecules.get(i);
}
/** Return the first molecule that matches the specified name. */
public Molecule getMolecule(String name){
for(Molecule molecule : molecules){
String moleculeName = molecule.getName();
if(name.equals(moleculeName)){
return molecule;
}
}
return null;
}
/** Return the List of molecules. */
public List<Molecule> getMolecules(){
return Collections.unmodifiableList(molecules);
}
/** Remove a molecule. */
public void removeMoleculeByName(String pattern){
for(Iterator<Molecule> it = molecules.iterator(); it.hasNext(); ){
Molecule molecule = it.next();
String moleculeName = molecule.getName();
if(pattern.equals(moleculeName)){
fireMoleculeRemovedEvent(molecule);
it.remove();
System.out.println("removed " + moleculeName);
}
}
}
/** Remove a molecule. */
public void removeMolecule(String pattern){
for(Iterator<Molecule> it = molecules.iterator(); it.hasNext(); ){
Molecule molecule = it.next();
if(moleculeMatches(pattern, molecule)){
fireMoleculeRemovedEvent(molecule);
it.remove();
}
}
}
private boolean moleculeMatches(String pattern, Molecule mol){
if(pattern.charAt(0) == '#'){
int moleculeCount = getMoleculeCount();
int id = Integer.parseInt(pattern.substring(1)) % moleculeCount;
for(int m = 0; m < moleculeCount; m++){
if(getMolecule(m) == mol && m == id){
return true;
}
}
}else{
return match.matches(pattern, mol.getName());
}
return false;
}
/** Remove maps. */
public void removeMap(String pattern){
for(Iterator<Map> it = maps.iterator(); it.hasNext(); ){
Map map = it.next();
String mapName = map.getName();
if(match.matches(pattern, mapName)){
String mapPrefix = mapName;
removeGraphicalObjectsBeginningWith(mapPrefix);
it.remove();
}
}
}
public void removeMap(Map map){
if(maps.contains(map)){
String name = map.getName();
removeGraphicalObjectsBeginningWith(name);
maps.remove(map);
}
}
/** Add a map to the scene. */
public void addMap(Map map){
maps.add(map);
int mapCount = maps.size();
String mapType = null;
String filename = map.getFile();
// try and guess map type from name
if(filename != null){
String lowercase = filename.toLowerCase();
if(lowercase.indexOf("2fofc") != -1){
mapType = "2fofc";
}else if(lowercase.indexOf("fofc") != -1){
mapType = "fofc";
}
}
// we couldn't figure it out from the name
// so if it is first map make it a 2fofc
// if it is second map make it fofc
if(mapType == null){
if(mapCount == 1){
mapType = "2fofc";
}else{
mapType = "fofc";
}
}
if(mapType == null){
mapType = "fofc";
}
Log.info("mapType " + mapType);
if(map.initialiseContours){
for(int i = 0; i < contourLevelCount; i++){
String prefix = mapType + "." + i;
String levelLabel = prefix + ".level";
String levelString = (String)properties.get(levelLabel);
double level = 3.0;
if(levelString != null){
level = FILE.readDouble(levelString);
}
map.setContourLevel(i, level);
String colourLabel = prefix + ".colour";
String colourString = (String)properties.get(colourLabel);
int colour = Color32.blue;
if(colourString != null){
colour = Color32.getColorFromName(colourString);
}
map.setContourColor(i, colour);
String displayLabel = prefix + ".displayed";
String displayString = (String)properties.get(displayLabel);
boolean display = false;
if(displayString != null &&
"true".equals(displayString)){
display = true;
}
map.setContourDisplayed(i, display);
String styleLabel = prefix + ".style";
String styleString = (String)properties.get(styleLabel);
int style = Map.Lines;
if(styleString != null){
if("solid".equals(styleString)){
style = Map.Surface;
}else if("lines".equals(styleString)){
style = Map.Lines;
}
}
map.setContourStyle(i, style);
}
}
for(int i = 0; i < contourLevelCount; i++){
// create the graphical object once
String contourName =
getContourGraphicalObjectName(map, i);
Tmesh contourObject = new Tmesh();
contourObject.setName(contourName);
addGraphicalObject(contourObject);
}
// fix the rereading of maps when we load another map
readMap(map);
for(int j = 0; j < Map.MaximumContourLevels; j++){
contourMap(map, j);
}
fireMapAddedEvent(map);
}
/** Change the line width. */
public void setMapContourTransparency(String name, int contour, int t){
Map map = getMap(name);
if(map != null){
Tmesh contourObject =
getContourGraphicalObject(map, contour);
if(contourObject != null){
contourObject.setTransparency(t);
}
}
}
/** Change the line width. */
public void setMapContourLineWidth(String name, int contour, double w){
Map map = getMap(name);
if(map != null){
Tmesh contourObject =
getContourGraphicalObject(map, contour);
if(contourObject != null){
contourObject.setLineWidth(w);
}
}
}
/** Change the value of a contour level. */
public void setMapContourLevel(String name, int contour, double level){
Map map = getMap(name);
if(map != null){
map.setContourLevel(contour, level);
//map.setContourDisplayed(contour, true);
if(map.getContourDisplayed(contour)){
map.setCenter(renderer.getCenter());
map.setRadius(mapRadius);
//determineRegion(map);
contourMap(map, contour);
}else{
getContourGraphicalObject(map, contour);
}
}
}
/** Change the value of a contour level. */
public void setMapContourColour(String name, int contour, int colour){
Map map = getMap(name);
if(map != null){
map.setContourColor(contour, colour);
if(map.getContourDisplayed(contour)){
map.setCenter(renderer.getCenter());
map.setRadius(mapRadius);
contourMap(map, contour);
}
}
}
/** Turn contour level on/off of a contour level. */
public void setMapContourDisplayed(String name,
int contour, int visible){
Map map = getMap(name);
if(map != null){
if(visible == 2){
// toggle
boolean current = map.getContourDisplayed(contour);
if(current){
map.setContourDisplayed(contour, false);
}else{
map.setContourDisplayed(contour, true);
}
}else{
boolean vis = false;
if(visible == 1){
vis = true;
}
map.setContourDisplayed(contour, vis);
}
if(map.getContourDisplayed(contour)){
map.setCenter(renderer.getCenter());
map.setRadius(mapRadius);
contourMap(map, contour);
}else{
Tmesh contourObject =
getContourGraphicalObject(map, contour);
contourObject.setVisible(false);
}
}
}
/** Return the total count of maps. */
public int getMapCount(){
return maps.size();
}
/** Return the specified map. */
public Map getMap(int i){
return maps.get(i);
}
/** Return the specified map from its name. */
public Map getMap(String name){
for(Map map : maps){
if(map.getName().equals(name)){
return map;
}
}
return null;
}
/** Return the specified map from its name. */
public List<Map> getMaps(String name){
List<Map> mapArray = new ArrayList<Map>(10);
for(Map map : maps){
if(match.matches(name, map.getName())){
mapArray.add(map);
}
}
return mapArray;
}
/** Get a residue iterator. */
public ResidueIterator getResidueIterator(){
return new ResidueIterator(this);
}
/** Get a atom iterator. */
public AtomIterator getAtomIterator(){
return new AtomIterator(this);
}
/** Should we display the atom label. */
private boolean displayAtomLabel = true;
/** Add a selected atom. */
public void addSelectedAtom(Atom atom){
String atomSelect = atom.selectStatement();
if(!selectedAtoms.contains(atom)){
selectedAtoms.add(atom);
execute("append " + atomSelect + ";");
fireAtomSelectedEvent(atom);
}else{
selectedAtoms.remove(atom);
execute("exclude " + atomSelect + ";");
}
if(pickedAtoms.contains(atom)){
pickedAtoms.remove(atom);
}else{
pickedAtoms.add(atom);
}
}
/** Add a pair of bump atoms. */
private void addBumpPair(Atom atom1, Atom atom2){
// only add if they are in the same molecule
if((bumpInSameMolecule && atom1.getMolecule() == atom2.getMolecule()) ||
!bumpInSameMolecule){
bumpAtoms.add(atom1);
bumpAtoms.add(atom2);
}
}
/** Generate the bump atoms around the specified atom. */
public void generateBumps(Atom atom){
List<Atom> bumpAtoms = Collections.singletonList(atom);
generateBumps(bumpAtoms);
}
/** Generate bumps for the specified set of atoms. */
public void generateBumps(List<Atom> bumpAtoms){
removeAllBumpAtoms();
int bumpAtomCount = bumpAtoms.size();
if(displayBumps && bumpAtomCount > 0){
List<Atom> sphereAtoms =
getAtomsAroundSelection(bumpAtoms, 5.0, true);
for(Atom sphereAtom : sphereAtoms){
double vdwRadius = sphereAtom.getVDWRadius();
for(Atom atom: bumpAtoms){
double atomVDWRadius = atom.getVDWRadius();
if(sphereAtom != atom &&
!atom.hasBond(sphereAtom) &&
!atom.connected13(sphereAtom) &&
!atom.connected14(sphereAtom)){
double dSq = atomVDWRadius + vdwRadius;
dSq *= dSq;
if(atom.distanceSquared(sphereAtom) < dSq){
addBumpPair(atom, sphereAtom);
}
}
}
}
}
}
/** Add a distance monitor to the list. */
private void addDistance(Distance d){
distances.add(d);
fireGenericAddedEvent((Generic)d);
}
/** Add a distance monitor between the selected atoms. */
public void addDistanceBetweenSelectedAtoms(){
int selectedAtomCount = selectedAtoms.size();
if(selectedAtomCount == 2){
Atom firstAtom = selectedAtoms.get(0);
Atom secondAtom = selectedAtoms.get(1);
Distance d = Distance.createDistanceMonitor(firstAtom,
secondAtom);
addDistance(d);
}
}
/** Add a pair of atoms to the distance list. */
public void addDistance(Atom firstAtom, Atom secondAtom){
// reuse bond for maintaining a distance.
Distance distance = new Distance();
distance.group0.add(firstAtom);
distance.group1.add(secondAtom);
addDistance(distance);
}
private List<Atom> angles = new ArrayList<Atom>(12);
/** Add a set of atoms to the angle list. */
private void addAngle(Atom firstAtom, Atom secondAtom,
Atom thirdAtom){
angles.add(firstAtom);
angles.add(secondAtom);
angles.add(thirdAtom);
}
/** Add a set of atoms to the angle list. */
private void addTorsion(Atom firstAtom, Atom secondAtom,
Atom thirdAtom, Atom fourthAtom){
torsions.add(firstAtom);
torsions.add(secondAtom);
torsions.add(thirdAtom);
torsions.add(fourthAtom);
}
/** Remove everything from the renderer. */
public void reset(){
removeGraphicalObjects("*");
removeMolecule("*");
removeMaps();
removeAllAngles();
removeAllDistances();
removeAllTorsions();
removeAllLabelledAtoms();
removeAllSelectedAtoms();
removeAllBumpAtoms();
removeSpaceGroup();
}
/** Remove all the angles. */
public void removeAllAngles(){
angles.clear();
}
/** Remove all the distance monitors. */
public void removeAllDistances(){
deleteDistances("*");
}
/** Remove all the torsion monitors. */
public void removeAllTorsions(){
torsions.clear();
}
/** Remove all labelled atoms. */
public void removeAllLabelledAtoms(){
AtomIterator atomIterator = getAtomIterator();
while(atomIterator.hasMoreElements()){
Atom atom = atomIterator.getNextAtom();
atom.setLabelled(false);
}
}
/** Remove all selected atoms. */
public void removeAllSelectedAtoms(){
execute("select none;");
selectedAtoms.clear();
pickedAtoms.clear();
fireAtomSelectedEvent(null);
}
/** Remove all bump atoms. */
private void removeAllBumpAtoms(){
bumpAtoms.clear();
}
/** Generate symmetry for the molecules we are displaying. */
private Molecule generateSymmetry(Point3d center, double radius){
Molecule symmetryMolecule = null;
if(displaySymmetry && !molecules.isEmpty()){
// figure out which symmetry we want to use
determineSymmetry();
if(symmetry != null){
symmetryMolecule =
generateSymmetryMolecule(center, radius);
}
}
return symmetryMolecule;
}
/** Actually generate the symmetry atoms. */
private Molecule generateSymmetryMolecule(Point3d center, double radius){
Molecule symmetryMolecule = new Molecule();
symmetryMolecule.setName("Symmetry");
symmetryMolecule.setMoleculeType(Molecule.SymmetryMolecule);
Point3d fractionalCenter = center.clone();
Molecule molecule = getMolecule(0);
Point3d moleculeCenter = molecule.getCenter();
double moleculeRadius = molecule.getRadius();
int atomCount = molecule.getAtomCount();
int oldIds[] = new int[atomCount];
// record the current atom ids
// as we need to put them back to
// prevent atom id selection expressions
// breaking, there must be a better way
// of fixing this.
for(int a = 0; a < atomCount; a++){
Atom atom = molecule.getAtom(a);
oldIds[a] = atom.getId();
}
// make sure the atom numbers start at 0
molecule.assignAtomNumbers();
// get the matrices for our chosen symmetry.
Matrix cartesianToFractional =
symmetry.getCartesianToFractionalMatrix();
Matrix fractionalToCartesian =
symmetry.getFractionalToCartesianMatrix();
// figure out where the molecule was located in
// fractional unit cells.
fractionalCenter.transform(cartesianToFractional);
// if the point has -ve coords we need to subtract 1
int originx = (int)fractionalCenter.x;
if(fractionalCenter.x < 0.0) originx -= 1;
int originy = (int)fractionalCenter.y;
if(fractionalCenter.y < 0.0) originy -= 1;
int originz = (int)fractionalCenter.z;
if(fractionalCenter.z < 0.0) originz -= 1;
// the symmetry operators
List<Matrix> symmetryOperators = symmetry.getSymmetryOperators();
Point3d currentCenter = new Point3d();
int operatorCount = symmetryOperators.size();
Matrix currentTransform = new Matrix();
int unit_cell_offset = 2;
// now iterate over 1 unit cell in each direction along
// each fractional axis
for(int ix = -unit_cell_offset; ix <= unit_cell_offset; ix++){
for(int iy = -unit_cell_offset; iy <= unit_cell_offset; iy++){
for(int iz = -unit_cell_offset; iz <= unit_cell_offset; iz++){
int startOperator = 0;
// if we're in the primary unit cell
// we don't need the identity matrix
if(ix == 0 && iy == 0 && iz == 0){
startOperator = 1;
}
for(int s = startOperator; s < operatorCount; s++){
Matrix operator = symmetryOperators.get(s);
currentTransform.setIdentity();
currentTransform.transform(cartesianToFractional);
// shift to origin
currentTransform.translate(-originx, -originy, -originz);
currentTransform.transform(operator);
currentTransform.translate(ix, iy, iz);
// and shift back to original unit cell
currentTransform.translate(originx, originy, originz);
currentTransform.transform(fractionalToCartesian);
currentCenter.set(moleculeCenter);
currentCenter.transform(currentTransform);
double d = currentCenter.distance(center);
if(d < radius + moleculeRadius){
addSymmetryAtoms(molecule, center, radius,
currentTransform,
symmetryMolecule);
}
}
}
}
}
symmetryMolecule.connect();
// restore the original atom ids.
for(int a = 0; a < atomCount; a++){
Atom atom = molecule.getAtom(a);
atom.setId(oldIds[a]);
}
return symmetryMolecule;
}
/** Add the symmetry atoms that overlap. */
private void addSymmetryAtoms(Molecule molecule, Point3d center,
double radius, Matrix transform,
Molecule symmetryMolecule){
int atomCount = molecule.getAtomCount();
int inSphere[] = new int[atomCount];
Point3d p = new Point3d();
double radiusSq = radius * radius;
for(int a = 0; a < atomCount; a++){
if(inSphere[a] == 0){
Atom atom = molecule.getAtom(a);
p.set(atom);
p.transform(transform);
if(p.distanceSquared(center) < radiusSq){
inSphere[a] = 1;
}
}
}
expandSelection(molecule, inSphere);
Residue lastResidue = null;
Chain lastChain = null;
for(int a = 0; a < atomCount; a++){
if(inSphere[a] != 0){
Atom atom = molecule.getAtom(a);
p.set(atom);
p.transform(transform);
Residue res = atom.getResidue();
Chain chain = res.getParent();
if(chain != lastChain){
Chain newChain = symmetryMolecule.addChain();
newChain.setName(chain.getName());
}
if(res != lastResidue){
Residue newResidue = symmetryMolecule.addResidue();
newResidue.setNumber(res.getNumber());
newResidue.setInsertionCode(res.getInsertionCode());
newResidue.setName(res.getName());
}
lastResidue = res;
lastChain = chain;
Atom newAtom = symmetryMolecule.addAtom();
newAtom.set(p);
newAtom.setElement(atom.getElement());
newAtom.setAtomLabel(atom.getAtomLabel());
}
}
}
/** Return the total number of atoms in the renderer. */
public int getAtomCount(){
int moleculeCount = molecules.size();
int atomCount = 0;
for(int m = 0; m < moleculeCount; m++){
Molecule molecule = molecules.get(m);
atomCount += molecule.getAtomCount();
}
return atomCount;
}
public int setMoleculeVariable(String pattern, String name, String value){
for(Molecule m : molecules){
if(moleculeMatches(pattern, m)){
if("bonddetails".equalsIgnoreCase(name)){
m.setBoolean(Molecule.DisplayBondDetails, "on".equalsIgnoreCase(value));
}else{
throw new RuntimeException("unknown molecule variable: " + name);
}
}
}
return 0;
}
/** Change molecule visibility. */
public int setMoleculeVisibility(String pattern, String action){
for(Molecule m : molecules){
if(moleculeMatches(pattern, m)){
if("off".equals(action)){
m.setDisplayed(0);
}else if("on".equals(action)){
m.setDisplayed(1);
}else if("toggle".equals(action)){
m.setDisplayed(2);
}else if("trace".equals(action)){
m.setDisplayStyle(Molecule.Trace);
}else if("tracealways".equals(action)){
m.setDisplayStyle(Molecule.TraceAlways);
}else if("normal".equals(action)){
m.setDisplayStyle(Molecule.Normal);
}else{
System.out.println("molecule display: unknown option " +
action);
return 1;
}
}
}
return 0;
}
/** Expand the selected atoms to include the whole residue. */
private void expandSelection(Molecule molecule, int inSphere[]){
int atomCount = molecule.getAtomCount();
for(int a = 0; a < atomCount; a++){
Atom atom = molecule.getAtom(a);
int id = atom.getId();
if(inSphere[id] == 1){
int bondCount = atom.getBondCount();
for(int b = 0; b < bondCount; b++){
Atom bondedAtom = atom.getBondedAtom(b);
int bondedId = bondedAtom.getId();
inSphere[bondedId] = 2;
}
}
}
}
/** Determine the set of symmetry operators. */
private void determineSymmetry(){
// this is more difficult than it appears as the
// molecules may not have a spacegroup specified.
if(symmetry == null){
for(Map map : maps){
if(map.getMapType() != Map.MapType.O_BINARY &&
map.getMapType() != Map.MapType.INSIGHT_ASCII){
// we don't want symmetry if its p1
if(map.getSpaceGroupNumber() > 1){
System.out.println("map number " +
map.getSpaceGroupNumber());
symmetry = map;
break;
}else{
symmetry = null;
}
}
}
}
if(symmetry == null){
for(Molecule molecule : molecules){
Symmetry moleculeSymmetry = molecule.getSymmetry();
if(moleculeSymmetry != null){
symmetry = moleculeSymmetry;
break;
}
}
}
}
/* Methods for performing various kinds of atom selections. */
private Stack<Atom> selectionStack = new Stack<Atom>();
/** Push a selection onto the stack. */
public void pushSelection(List<Atom> atoms){
selectionStack.addAll(atoms);
}
/** Pop a selection from the selection stack. */
public List<Atom> popSelection(){
List<Atom> atoms = null;
int selectionStackSize = selectionStack.size();
if(selectionStackSize > 0){
atoms = Collections.singletonList(selectionStack.pop());
}else{
Log.warn("can't pop, stack is empty");
}
// always return something.
if(atoms == null){
atoms = Collections.emptyList();
}
return atoms;
}
/** Peek a selection on the stack. */
public List<Atom> peekSelection(int i){
List<Atom> atoms = null;
int selectionStackSize = selectionStack.size();
if(i < selectionStackSize){
if (i == 0) {
atoms = Collections.singletonList(selectionStack.peek());
} else {
atoms = Collections.singletonList(selectionStack.get(selectionStackSize -i -1));
}
}else{
Log.warn("can't peek selection " + i +
": stack is only " + selectionStackSize + " deep");
}
// always return something.
if(atoms == null){
atoms = Collections.emptyList();
}
return atoms;
}
/** Evaluate a selection expression. */
public List<Atom> getAtomsInSelection(String expression){
List<Atom> selectedAtoms = new ArrayList<Atom>();
String residueExpressions[] = FILE.split(expression, " ");
for(int i = 0; i < residueExpressions.length; i++){
List<Atom> thisSelection = getAtomsInResidue(residueExpressions[i]);
for(Atom a : thisSelection){
selectedAtoms.add(a);
}
}
return selectedAtoms;
}
/** Return the atoms in a particular residue. */
private List<Atom> getAtomsInResidue(String residueSpecification){
List<Atom> selectedAtoms = new ArrayList<Atom>(20);
String chainName = null;
int stopResidueNumber[] = null;
int startResidueNumber[] = null;
int residueRangeCount = 0;
int atomNameCount = 0;
String atomNames[] = null;
String residueString = null;
String remainder = null;
String moleculeName = null;
double radius = -1.0;
if(residueSpecification.equalsIgnoreCase("current")){
List<Atom> currentSelection = getSelectedAtoms();
for(Atom atom : currentSelection){
selectedAtoms.add(atom);
}
return selectedAtoms;
}else if(residueSpecification.equalsIgnoreCase("all")){
AtomIterator iterator = getAtomIterator();
while(iterator.hasMoreElements()){
Atom atom = iterator.getNextAtom();
selectedAtoms.add(atom);
}
return selectedAtoms;
}
String colonTokens[] = FILE.split(residueSpecification, ":");
if(residueSpecification.startsWith("id:") && colonTokens.length == 3){
// its a specific atom id selection
String molName = colonTokens[1];
List<Atom> idSelection = new ArrayList<Atom>(20);
for(Molecule molecule : molecules){
if(molName.equals(molecule.getName())){
String idTokens[] = FILE.split(colonTokens[2], ",");
int idCount = idTokens.length;
int startId[] = new int[idCount];
int stopId[] = new int[idCount];
for(int id = 0; id < idCount; id++){
String ids[] = FILE.split(idTokens[id], "-");
startId[id] = FILE.readInteger(ids[0]);
if(ids.length == 1){
stopId[id] = startId[id];
}else if(ids.length == 2){
stopId[id] = FILE.readInteger(ids[1]);
}
}
int atomCount = molecule.getAtomCount();
for(int a = 0; a < atomCount; a++){
Atom atom = molecule.getAtom(a);
int atomId = atom.getId();
for(int id = 0; id < idCount; id++){
if(atomId >= startId[id] && atomId <= stopId[id]){
idSelection.add(atom);
break;
}
}
}
}
}
return idSelection;
}
if(debug){
System.out.println("colonTokens.length " + colonTokens.length);
for(int i = 0; i < colonTokens.length; i++){
System.out.println("token "+ colonTokens[i]);
}
}
if(colonTokens.length == 1){
remainder = colonTokens[0];
}else if(colonTokens.length == 2){
chainName = colonTokens[0];
remainder = colonTokens[1];
}else if(colonTokens.length == 3){
moleculeName = colonTokens[0];
chainName = colonTokens[1];
remainder = colonTokens[2];
}else if(colonTokens.length == 4){
radius = FILE.readDouble(colonTokens[0]);
moleculeName = colonTokens[1];
chainName = colonTokens[2];
remainder = colonTokens[3];
}
String dotTokens[] = FILE.split(remainder, ".");
residueString = dotTokens[0];
if(dotTokens.length == 2){
atomNames = FILE.split(dotTokens[1], ",");
atomNameCount = atomNames.length;
}
if((residueString != null) &&
(!("*".equals(residueString) || residueString.length() == 0))){
String residueRanges[] = FILE.split(residueString, ",");
residueRangeCount = residueRanges.length;
startResidueNumber = new int[residueRangeCount];
stopResidueNumber = new int[residueRangeCount];
for(int i = 0; i < residueRangeCount; i++){
String residueTokens[] = FILE.split(residueRanges[i], "-");
startResidueNumber[i] = FILE.readInteger(residueTokens[0]);
if(residueTokens.length == 2){
stopResidueNumber[i] = FILE.readInteger(residueTokens[1]);
}else{
stopResidueNumber[i] = startResidueNumber[i];
}
}
}
if(chainName != null){
// replace ^ characters with space
chainName = chainName.replace('^', ' ');
if("*".equals(chainName) || chainName.isEmpty()){
chainName = null;
}
}
if(moleculeName != null &&
moleculeName.isEmpty()){
moleculeName = null;
}
if(debug){
System.out.println("radius " + radius);
System.out.println("moleculeName " + moleculeName);
System.out.println("chainName " + moleculeName);
System.out.println("remainder " + remainder);
}
for(Molecule molecule : molecules){
int chainCount = molecule.getChainCount();
if(moleculeName == null ||
moleculeMatches(moleculeName, molecule)){
//match.matches(moleculeName, molecule.getName())){
for(int c = 0; c < chainCount; c++){
Chain chain = molecule.getChain(c);
int residueCount = chain.getResidueCount();
// check residues if no chain name is
// specified or if the chain names match
if(chainName == null ||
match.matches(chainName, chain.getName())){
for(int r = 0; r < residueCount; r++){
Residue residue = chain.getResidue(r);
int residueNumber = residue.getNumber();
int residueMatch = 0;
if(residueRangeCount == 0){
residueMatch = 1;
}else{
for(int i = 0; i < residueRangeCount; i++){
if(residueNumber >= startResidueNumber[i] &&
residueNumber <= stopResidueNumber[i]){
residueMatch = 1;
break;
}
}
}
if(residueMatch == 1){
int atomCount = residue.getAtomCount();
for(int a = 0; a < atomCount; a++){
Atom atom = residue.getAtom(a);
int matchedAtom = 0;
if(atomNameCount == 0){
matchedAtom = 1;
}else{
for(int i = 0; i < atomNameCount; i++){
if(match.matches(atomNames[i], atom.getAtomLabel())){
matchedAtom = 1;
break;
}
}
}
if(matchedAtom == 1){
selectedAtoms.add(atom);
}
}
}
}
}
}
}
}
// if there was a radius specification
if(radius > 0.0){
double radiusSq = radius * radius;
List<Atom> radiusSelection = new ArrayList<Atom>(20);
AtomIterator iterator = getAtomIterator();
while(iterator.hasMoreElements()){
Atom atom = iterator.getNextAtom();
for(Atom selectedAtom : selectedAtoms){
if(atom.distanceSquared(selectedAtom) < radiusSq){
radiusSelection.add(atom);
}
}
}
return radiusSelection;
}
return selectedAtoms;
}
/** Return the atoms in the specified sphere. */
private List<Atom> getAtomsInSphere(Point3d c, double r){
List<Atom> selectedAtoms = new ArrayList<Atom>(20);
double rSq = r * r;
for(Molecule molecule : molecules){
int atomCount = molecule.getAtomCount();
for(int a = 0; a < atomCount; a++){
Atom atom = molecule.getAtom(a);
if(atom.distanceSquared(c) < rSq){
selectedAtoms.add(atom);
}
}
}
return selectedAtoms;
}
/** Return atoms in a shell around the specified selection. */
private List<Atom> getAtomsAroundSelection(List<Atom> selection,
double radius,
boolean include){
List<Atom> newSelection = new ArrayList<Atom>(selection.size());
Point3d selectionCenter = getCenter(selection);
if(selectionCenter != null){
double selectionRadius = getRadius(selection, selectionCenter);
List<Atom> sphereSelection =
getAtomsInSphere(selectionCenter, selectionRadius + radius);
double radiusSq = radius * radius;
for(Atom atom : sphereSelection){
if(!include || !selection.contains(atom)){
for(Atom selectedAtom : selection){
if(atom.distanceSquared(selectedAtom) < radiusSq){
newSelection.add(atom);
break;
}
}
}
}
}
return newSelection;
}
/** Get atoms that are part of ligands. */
private void getAtomsInLigands(List<Atom> selectedAtoms){
AtomIterator atomIterator = getAtomIterator();
while(atomIterator.hasMoreElements()){
Atom atom = atomIterator.getNextAtom();
Residue residue = atom.getResidue();
if(!residue.isStandardAminoAcid() && !residue.isIon() &&
!residue.isNucleicAcid() && !residue.isSolvent() &&
atom.getBondCount() > 0){
selectedAtoms.add(atom);
}
}
}
/** Wrapper around getAtomsInLigands. */
public List<Atom> getAtomsInLigands(){
List<Atom> list = new ArrayList<Atom>(20);
getAtomsInLigands(list);
return list;
}
/** Return the number of selected atoms. */
private int getSelectedAtomCount(){
AtomIterator iterator = getAtomIterator();
int selectedAtomCount = 0;
while(iterator.hasMoreElements()){
Atom atom = iterator.getNextAtom();
if(atom.isSelected()){
selectedAtomCount++;
}
}
return selectedAtomCount;
}
/** Return the atoms that are currently selected. */
public List<Atom> getSelectedAtoms(){
return getSomeAtoms(true, false);
}
/** Return the atoms that are currently labelled or selected. */
public List<Atom> getSelectedOrLabelledAtoms(){
return getSomeAtoms(true, true);
}
/** Return the atoms that are currently labelled. */
private List<Atom> getSomeAtoms(boolean selected, boolean labelled){
List<Atom> selectedAtoms = new ArrayList<Atom>(30);
AtomIterator atomIterator = getAtomIterator();
while(atomIterator.hasMoreElements()){
Atom atom = atomIterator.getNextAtom();
if((labelled && atom.isLabelled()) ||
(selected && atom.isSelected())){
selectedAtoms.add(atom);
}
}
return selectedAtoms;
}
/** Set the specified atoms as selected. */
public void setSelected(List<Atom> selection){
setSelected(selection, false);
}
/** Set the specified atoms as selected. */
public void setSelected(List<Atom> selection, boolean exclude){
for(Atom atom : selection){
if(exclude){
atom.setSelected(false);
}else{
atom.setSelected(true);
}
}
// if we are displaying bumps then generate bumps when
// we change the selection
if(displayBumps){
List<Atom> selectedAtoms = getSelectedAtoms();
generateBumps(selectedAtoms);
}
}
/** Return the geometric center of the atoms in the selection. */
public Point3d getCenter(List<Atom> selection){
int atomCount = selection.size();
if(atomCount > 0){
Point3d center = new Point3d();
for(Atom atom : selection){
center.add(atom);
}
center.divide(atomCount);
return center;
}
return null;
}
/** Return the radius of the atoms in the selection. */
public double getRadius(List<Atom> selection){
return getRadius(selection, null);
}
/** Return the radius of the atoms in the selection. */
private double getRadius(List<Atom> selection, Point3d center){
double radius = 0.0;
int atomCount = selection.size();
if(atomCount > 0){
if(center == null){
center = getCenter(selection);
}
for(Atom atom : selection){
double distance = center.distance(atom);
if(distance > radius){
radius = distance;
}
}
}
return radius;
}
/** Reset the wide bonds flags. */
public void resetWideBonds(){
for(Molecule molecule : molecules){
int bondCount = molecule.getBondCount();
for(int b = 0; b < bondCount; b++){
Bond bond = molecule.getBond(b);
bond.setBondWidth(1);
}
}
}
/** Set the center for the renderer. */
public void setCenter(double x, double y, double z){
Point3d newCenter = new Point3d(x, y, z);
setCenter(newCenter);
}
public void setCenter(Point3d newCenter){
setCenter(newCenter, true);
}
/** Set the center for the renderer. */
public void setCenter(Point3d newCenter, boolean setClipping){
renderer.setCenter(newCenter);
if(setClipping){
setRadius(displayRadius);
setClip(displayRadius);
}
// recontour any maps that we might be displaying
generateMaps();
// generate the symmetry atoms.
generateSymmetry();
}
/** Set the center from a set of atoms. */
public void setCenter(List<Atom> selectedAtoms){
Point3d center = getCenter(selectedAtoms);
if(center != null){
setCenter(center);
double radius = getRadius(selectedAtoms, center);
setClip(radius);
setRadius(radius);
renderer.setZoom(1.0);
}
}
/** Generate the map display. */
private void generateMaps(){
if(displayMaps){
readMaps();
contourMaps();
}
}
/** Remove the displayed maps. */
private void removeContourLevels(){
removeGraphicalObjectsBeginningWith("Map");
}
/** Remove the maps rather than the contour levels. */
private void removeMaps(){
removeGraphicalObjectsBeginningWith("Map");
maps.clear();
}
/** Should we display maps. */
public void setDisplayMaps(boolean state){
displayMaps = state;
if(!displayMaps){
removeContourLevels();
}else{
generateMaps();
}
}
/** Set whether we display bumps. */
public void setDisplayBumps(boolean state){
displayBumps = state;
if(!displayBumps){
removeAllBumpAtoms();
}else{
List<Atom> selectedAtoms = getSelectedAtoms();
generateBumps(selectedAtoms);
}
}
public void setBumpInSameMolecule(boolean b){
bumpInSameMolecule = b;
}
/** Set the distance display flag. */
public void setDisplayDistances(boolean d){
displayDistances = d;
}
/** Return the distance display flag. */
public boolean getDisplayDistances(){
return displayDistances;
}
/** Set whether we display solvent. */
public void setDisplaySolvent(boolean state){
displaySolvent = state;
}
/** Return bonds where both atoms are in selectedAtoms. */
public List<Bond> getBondsInSelection(List<Atom> selectedAtoms){
List<Bond> selectedBonds = new ArrayList<Bond>(10);
AtomIterator iterator = getAtomIterator();
while(iterator.hasMoreElements()){
Atom atom = iterator.getNextAtom();
atom.setTemporarilySelected(false);
}
for(Atom a : selectedAtoms){
a.setTemporarilySelected(true);
}
for(Molecule molecule : molecules){
int bondCount = molecule.getBondCount();
for(int b = 0; b < bondCount; b++){
Bond bond = molecule.getBond(b);
Atom firstAtom = bond.getFirstAtom();
Atom secondAtom = bond.getSecondAtom();
if(firstAtom.isTemporarilySelected() &&
secondAtom.isTemporarilySelected()){
selectedBonds.add(bond);
}
}
}
return selectedBonds;
}
/** Set wide bonds for the atoms in the selection. */
public void setWideBonds(List<Atom> selectedAtoms){
resetWideBonds();
for(Atom atom : selectedAtoms){
atom.setTemporarilySelected(true);
}
for(Atom atom : selectedAtoms){
int bondCount = atom.getBondCount();
if(atom.isTemporarilySelected()){
for(int b = 0; b < bondCount; b++){
Bond bond = atom.getBond(b);
Atom otherAtom = bond.getOtherAtom(atom);
if(otherAtom.isTemporarilySelected()){
bond.setBondWidth(2);
}
}
}
}
for(Atom atom : selectedAtoms){
atom.setTemporarilySelected(false);
}
}
/** The maximum number of contour levels. */
private static int MaximumContourLevels = Map.MaximumContourLevels;
/** The number of contour levels we are using. */
private int contourLevelCount = 0;
/** Add a contour level. */
private void addContourLevel(){
if(contourLevelCount == MaximumContourLevels){
System.out.println("maximum number of contour levels exceeded");
return;
}
contourLevelCount++;
}
/** Reset the contour levels. */
private void resetContourLevels(){
contourLevelCount = 0;
}
/** Generate the symmetry copies of the molecules. */
private void generateSymmetry(){
removeSymmetry();
Molecule symmetry =
generateSymmetry(renderer.getCenter(), symmetryRadius);
if(symmetry != null){
assignSymmetryAtomColors(symmetry);
addMolecule(symmetry);
}
}
/** Remove the symmetry molecule if there is one. */
private void removeSymmetry(){
removeMolecule("Symmetry");
}
/** Assign atom colors. */
private void assignSymmetryAtomColors(Molecule molecule){
int atomCount = molecule.getAtomCount();
for(int a = 0; a < atomCount; a++){
Atom atom = molecule.getAtom(a);
if(atom.getElement() == PeriodicTable.CARBON){
atom.setColor(0xaaaaaa);
}
}
}
/** Read the maps that we are displaying. */
private void readMap(Map map){
map.setCenter(renderer.getCenter());
map.setRadius(mapRadius);
}
private void readMaps(){
for(Map map : maps){
if(map.hasContoursDisplayed()){
readMap(map);
}
}
}
/** Contour the maps that we are displaying. */
private void contourMaps(){
for(Map map : maps){
for(int j = 0; j < Map.MaximumContourLevels; j++){
contourMap(map, j);
}
}
}
/** Get name of contour for particular map. */
private String getContourGraphicalObjectName(Map map, int contour){
return map.getName() + "_" + contour;
}
/** Get tmesh of contour for particular map. */
public Tmesh getContourGraphicalObject(Map map, int contour){
String contourName = getContourGraphicalObjectName(map, contour);
Tmesh contourObject = renderer.getGraphicalObject(contourName);
return contourObject;
}
/** Generate one specific contour level for the map. */
public void contourMap(Map map, int contour){
String contourName = getContourGraphicalObjectName(map, contour);
if(map.getContourDisplayed(contour)){
if(map.needsReading()){
determineRegion(map);
map.setNeedsReading(false);
}
int style = map.getContourStyle(contour);
Tmesh contourObject =
contourRegion(map, contour, style);
contourObject.setColor(map.getContourColor(contour));
contourObject.setName(contourName);
contourObject.setVisible(true);
}else{
Tmesh contourObject = getContourGraphicalObject(map, contour);
contourObject.setVisible(false);
}
}
/** Figure out the region of the map we will contour. */
private void determineRegion(Map map){
map.read();
if(map.getMapType() == Map.MapType.CCP4_BINARY ||
map.getMapType() == Map.MapType.O_BINARY){
Matrix cartesianToFractional =
map.getCartesianToFractionalMatrix();
Point3d mapCenter = renderer.getCenter();
mapCenter.transform(cartesianToFractional);
mapCenter.x *= map.nv[0];
mapCenter.y *= map.nv[1];
mapCenter.z *= map.nv[2];
swapAxes(mapCenter, map);
map.centerGrid[0] = (int)(mapCenter.x);
map.centerGrid[1] = (int)(mapCenter.y);
map.centerGrid[2] = (int)(mapCenter.z);
for(int i = 0; i < 3; i++){
// subtract the map origin
map.centerGrid[i] -= map.nu[i];
map.minimumGrid[i] = map.centerGrid[i] - contourSize;
map.maximumGrid[i] = map.centerGrid[i] + contourSize;
// force the grid range to lie within the bounds of the map
if(map.minimumGrid[i] < 0){
map.minimumGrid[i] = 0;
}
if(map.maximumGrid[i] < 0){
map.maximumGrid[i] = 0;
}
if(map.minimumGrid[i] >= map.grid[i]){
map.minimumGrid[i] = map.grid[i];
}
if(map.maximumGrid[i] >= map.grid[i]){
map.maximumGrid[i] = map.grid[i];
}
}
// now read the region that we identified.
map.readRegion();
}
}
/** Contour the data that is in stored in the map. */
private Tmesh contourRegion(Map map, int contourNumber, int style){
int nx =0, ny=0, nz=0;
Map.MapType mapType = map.getMapType();
double level = map.getContourLevel(contourNumber);
Tmesh contour = getContourGraphicalObject(map, contourNumber);
// remove all the old points from the object.
contour.empty();
if(style == Map.Lines){
contour.style = Tmesh.Style.LINES;
}else if(style == Map.Surface){
contour.style = Tmesh.Style.TRIANGLES;
}
if(mapType == Map.MapType.CCP4_BINARY || mapType == Map.MapType.O_BINARY){
nx = map.maximumGrid[0] - map.minimumGrid[0];
ny = map.maximumGrid[1] - map.minimumGrid[1];
nz = map.maximumGrid[2] - map.minimumGrid[2];
}else{
nx = map.ngrid[0];
ny = map.ngrid[1];
nz = map.ngrid[2];
}
double rmsLevel = map.rms * level;
// make sure we weren't contouring
// off edge of grid
if(nx > 0 && ny > 0 && nz > 0){
if(style == Map.Lines){
March.generateTriangles = false;
March.surface(map.data, nx, ny, nz, (float)rmsLevel, false, contour);
}else if(style == Map.Surface){
boolean invert = (rmsLevel < 0.0)?true:false;
March.generateTriangles = true;
March.surface(map.data, nx, ny, nz, (float)rmsLevel, invert, contour);
}
}
// ok we got the contour back contoured on unit grid
// now transform all the coordinates into real space
transformContourPoints(map, contour);
return contour;
}
/** Transform the contour points into real space. */
private void transformContourPoints(Map map, Tmesh contour){
int pointCount = contour.np;
Point3d p = new Point3d();
Map.MapType mapType = map.getMapType();
if(mapType == Map.MapType.CCP4_BINARY || mapType == Map.MapType.O_BINARY){
for(int i = 0; i < pointCount; i++){
map.relativeGridToCartesian(contour.x[i], contour.y[i],
contour.z[i], p);
contour.x[i] = (float)p.x;
contour.y[i] = (float)p.y;
contour.z[i] = (float)p.z;
}
}else{
for(int i = 0; i < pointCount; i++){
contour.x[i] *= map.spacing.x;
contour.y[i] *= map.spacing.y;
contour.z[i] *= map.spacing.z;
contour.x[i] += map.origin.x;
contour.y[i] += map.origin.y;
contour.z[i] += map.origin.z;
}
}
if(contour.style == Tmesh.Style.TRIANGLES && mapType == Map.MapType.CCP4_BINARY){
// swap surface normals round according
// to the map header
// we will regenerate them later...
double xxx[] = new double[3];
double swapped[] = new double[3];
for(int i = 0; i < contour.np; i++){
xxx[0] = contour.nx[i];
xxx[1] = contour.ny[i];
xxx[2] = contour.nz[i];
swapped[map.axis[0]] = xxx[0];
swapped[map.axis[1]] = xxx[1];
swapped[map.axis[2]] = xxx[2];
contour.nx[i] = (float)swapped[0];
contour.ny[i] = (float)swapped[1];
contour.nz[i] = (float)swapped[2];
}
}
}
/** Map the point coordinates back into the map ordering. */
private void swapAxes(Point3d p, Map map){
double x = p.x, y = p.y, z = p.z;
if (map.axis[0] == 0) p.x = x;
else if(map.axis[0] == 1) p.x = y;
else if(map.axis[0] == 2) p.x = z;
if (map.axis[1] == 0) p.y = x;
else if(map.axis[1] == 1) p.y = y;
else if(map.axis[1] == 2) p.y = z;
if (map.axis[2] == 0) p.z = x;
else if(map.axis[2] == 1) p.z = y;
else if(map.axis[2] == 2) p.z = z;
}
/** Clip any maps we are displaying to show only unoccupied density. */
public void clipMaps(String namePattern, List<Atom> selection, boolean inside){
// reset the last atom to clip as we currently dont have one
lastAtom = null;
lastAtomClips = 0;
lastAtomBondedClips = 0;
clips = 0;
for(Map map : maps){
if(namePattern == null || match.matches(namePattern, map.getName())){
clipMap(map, selection, inside);
}
}
System.out.println("clips " + clips);
System.out.println("last atom clips " + lastAtomClips);
System.out.println("last atom bonded clips " + lastAtomBondedClips);
// recontour the maps we just clipped.
contourMaps();
}
/** Clip this map. */
private void clipMap(Map map, List<Atom> selection, boolean inside){
Point3d p = new Point3d();
float data[] = map.data;
int point = 0;
for(int iz = map.minimumGrid[2]; iz < map.maximumGrid[2]; iz++){
for(int iy = map.minimumGrid[1]; iy < map.maximumGrid[1]; iy++){
int gridStart = map.minimumGrid[0];
int gridStop = map.maximumGrid[0];
for(int ix = gridStart; ix < gridStop; ix++){
map.absoluteGridToCartesian(ix + map.nu[0],
iy + map.nu[1],
iz + map.nu[2], p);
if(clipped(p, selection) != inside){
data[point] = 0.0f;
}
point++;
}
}
}
}
private static final double clipDistance = 1.5;
private Atom lastAtom = null;
private int clips = 0;
private int lastAtomClips = 0;
private int lastAtomBondedClips = 0;
/** Is this point inside an atom in the molecule. */
private boolean clipped(Point3d p, List<Atom> selection){
double dSq = clipDistance * clipDistance;
// first check the last atom that clipped.
if(lastAtom != null){
if(p.distanceSquared(lastAtom) < dSq){
lastAtomClips ++;
return true;
}
lastAtom = null;
}
for(Atom atom : selection){
double dx = 0.0;
if(atom.x > p.x){
dx = atom.x - p.x;
}else{
dx = p.x - atom.x;
}
if(dx < clipDistance &&
p.distanceSquared(atom) < dSq){
lastAtom = atom;
clips++;
return true;
}
}
return false;
}
/** Are shadows on. */
public boolean shadows = false;
/** Has something changed the scene. */
public boolean dirty = false;
/** Render passes. */
private Renderer.ShadowMode renderPasses[] = new Renderer.ShadowMode[2];
/** Paint the rendered image into the screen. */
public synchronized void paint(){
if(!dirty){
return;
}
int passCount = 1;
if(shadows){
passCount = 2;
renderPasses[0] = Renderer.ShadowMode.ShadowsAccumulate;
renderPasses[1] = Renderer.ShadowMode.ShadowsOn;
}else{
passCount = 1;
renderPasses[0] = Renderer.ShadowMode.ShadowsOff;
}
// this paint method needs completely restructuring
// this object should have a method registered with
// the renderer that calls back to here.
for(int i = 0; i < passCount; i++){
renderer.shadowMode = renderPasses[i];
renderer.redraw();
if(!molecules.isEmpty()){
drawMolecules();
}
renderer.drawObjects();
drawMaps();
renderer.postProcess();
}
dirty = false;
}
private void drawMaps(){
for(Map map : maps){
if(map.volumeRender){
drawMap(map);
}
}
}
private int splatKernel[] = null;
private double s[] = {0., 0., 0.};
private void drawMap(Map map){
int gp = 0;
double overallScale = renderer.getOverallScale();
// seems to be sufficient
double spacing2 = map.spacing.x * 2.0;
int pixels = (int)(spacing2 * overallScale + 0.5);
int pixels2 = (pixels/2) * (pixels/2);
int splatSize = 2 * pixels + 1;
// allocate splat kernel
if(splatKernel == null ||
splatKernel.length < splatSize*splatSize){
splatKernel = new int[splatSize * splatSize];
}
int index = 0;
for(int iy = -pixels; iy <= pixels; iy++){
for(int ix = -pixels; ix <= pixels; ix++){
splatKernel[index] =
(int)(255.0 * Math.exp(- ( (ix*ix)/(double)pixels2 + (iy*iy)/(double)pixels2)));
index++;
}
}
double emax = map.volumeMax * map.getSigma();
double emin = map.volumeMin * map.getSigma();
int color = map.volumeColor;
int minTransp = 3;
for(int k = 0; k < map.ngrid[2]; k++){
double z = map.origin.z + k * map.spacing.z;
for(int j = 0; j < map.ngrid[1]; j++){
double y = map.origin.y + j * map.spacing.y;
for(int i = 0; i < map.ngrid[0]; i++){
double x = map.origin.x + i * map.spacing.x;
double v = map.data[gp];
int op = (int)(255 * (emax - v)/(emax - emin));
if(op < 256 && op >= minTransp){
renderer.applyTransform(x, y, z, s);
int xs = (int)s[0];
int ys = (int)s[1];
if(xs > -pixels && xs < renderer.pixelWidth + pixels &&
ys > -pixels && ys < renderer.pixelHeight + pixels){
// seems this is the way to get the z-coordinate right...
int zs = (int)(s[2] * (1<< (Renderer.FixedBits+8)));
if(zs < renderer.frontClip &&
zs > renderer.backClip){
for(int iy = -pixels; iy <= pixels; iy++){
int yp = ys + iy;
if(yp >= 0 && yp < renderer.pixelHeight){
index = (iy + pixels) * splatSize;
for(int ix = -pixels; ix <= pixels; ix++){
int xp = xs + ix;
if(xp >= 0 && xp < renderer.pixelWidth){
int intensity = (op * splatKernel[index]) >> 8;
if(intensity > 0 ){
if(intensity > 255){
intensity = 255;
}
renderer.blendPixel2(xp, yp, zs,
color, intensity);
}
}
index++;
}
}
}
}
}
}
gp++;
}
}
}
}
/** Figure out the molecule center and scale to make it just fit. */
private void initialiseCenter(){
if(renderer.getCenter() == null || true){
double xmin = Double.POSITIVE_INFINITY;
double xmax = Double.NEGATIVE_INFINITY;
double ymin = Double.POSITIVE_INFINITY;
double ymax = Double.NEGATIVE_INFINITY;
double zmin = Double.POSITIVE_INFINITY;
double zmax = Double.NEGATIVE_INFINITY;
for(Molecule molecule : molecules){
for(int a = 0; a < molecule.getAtomCount(); a++){
Atom atom = molecule.getAtom(a);
if(!atom.isSolvent()){
double x = atom.x;
double y = atom.y;
double z = atom.z;
xmin = Math.min(xmin, x);
ymin = Math.min(ymin, y);
zmin = Math.min(zmin, z);
xmax = Math.max(xmax, x);
ymax = Math.max(ymax, y);
zmax = Math.max(zmax, z);
}
}
}
double xCenter = 0.5 * (xmin + xmax);
double yCenter = 0.5 * (ymin + ymax);
double zCenter = 0.5 * (zmin + zmax);
Point3d moleculeCenter = new Point3d(xCenter, yCenter, zCenter);
renderer.setCenter(moleculeCenter);
}
if(false || renderer.getRadius() == 0.0){
// determine the radius.
double radius = 0.0;
Point3d moleculeCenter = renderer.getCenter();
for(Molecule molecule : molecules){
for(int a = 0; a < molecule.getAtomCount(); a++){
Atom atom = molecule.getAtom(a);
if(!atom.isSolvent()){
double distance = atom.distance(moleculeCenter);
if(distance > radius){
radius = distance;
}
}
}
}
setClip(radius);
radius *= 1.05;
setRadius(radius);
}
}
/** Reset the transformation matrix. */
private void resetTransformationMatrix(){
renderer.rotationMatrix.setIdentity();
}
/** Reset the view of the renderer. */
public void resetView(){
renderer.resetCenterAndRadius();
initialiseCenter();
resetTransformationMatrix();
renderer.setZoom(1.0);
}
/** Print the matrix out. */
public void printMatrix(){
renderer.rotationMatrix.print("matrix");
StringBuffer command = new StringBuffer(200);
command.append("animate\n\t-mode\t\trecenter\n\t-matrix\t\t\"");
Matrix m = renderer.rotationMatrix;
command.append(String.format("%g,%g,%g,%g", m.m00, m.m01, m.m02, m.m03));
command.append(String.format(",%g,%g,%g,%g", m.m10, m.m11, m.m12, m.m13));
command.append(String.format(",%g,%g,%g,%g", m.m20, m.m21, m.m22, m.m23));
command.append(String.format(",%g,%g,%g,%g", m.m30, m.m31, m.m32, m.m33));
Point3d p = renderer.getCenter();
command.append("\"\n\t-center\t\t\"");
command.append(String.format("%g,%g,%g", p.x, p.y, p.z));
command.append("\"\n");
command.append(String.format("\t-radius\t\t%g\n", (renderer.width/renderer.getZoom())));
command.append(String.format("\t-clipfront\t%g\n", renderer.front));
command.append(String.format("\t-clipback\t%g\n", renderer.back));
command.append("\t-steps\t\t10\n\t;\n");
System.out.println("\nCut and paste the command below to recreate the current view");
System.out.println("Make sure you include the ;\n");
System.out.println(command);
}
/** The length of a cross for an atom with no bonds. */
private static double crossLength = 0.4;
private List<Atom> sphereAtoms = new ArrayList<Atom>(512);
/** Apply the current transform to the molecule. */
private void transformMolecule(){
renderer.buildOverallMatrix();
int crossPixels = (int)(crossLength * renderer.getOverallScale());
int boxPixels = (int)(boxSize * renderer.getOverallScale());
if(boxPixels == 0){
boxPixels = 1;
}
sphereAtoms.clear();
int size[] = new int[2];
for(Molecule molecule : molecules){
boolean displayHydrogens = molecule.getBoolean(Molecule.DisplayHydrogens, false);
if(molecule.getDisplayed()){
int style = molecule.getDisplayStyle();
boolean normal = (style & Molecule.Normal) > 0;
int chainCount = molecule.getChainCount();
for(int c = 0; c < chainCount; c++){
Chain chain = molecule.getChain(c);
int residueCount = chain.getResidueCount();
for(int r = 0; r < residueCount; r++){
Residue res = chain.getResidue(r);
int atomCount = res.getAtomCount();
for(int a = 0; a < atomCount; a++){
Atom atom = res.getAtom(a);
if(displayHydrogens ||
atom.getElement() != PeriodicTable.HYDROGEN){
atom.transformToScreen(renderer.overallMatrix);
if(atom.attributes.contains(Atom.Attribute.VDWSphere)){
sphereAtoms.add(atom);
}
if(atom.attributes.contains(Atom.Attribute.BallAndStick)){
renderer.drawSphere(atom.x, atom.y, atom.z,
atom.getBallRadius(),
atom.getSelectedColor());
}
if(atom.attributes.contains(Atom.Attribute.Cylinder) &&
atom.getBondCount() == 0){
renderer.drawAccurateSphere(atom.x, atom.y, atom.z,
atom.getBallRadius(),
atom.getSelectedColor(), 255);
}
if(atom.isSimpleDisplayed()){
if(normal && atom.getBondCount() == 0){
drawAtom(atom, crossPixels);
}
if(atom.hasAttributes()){
if(atom.isLabelled() && displayAtomLabel){
int color = Color32.white;
if(renderer.getBackgroundColor() ==
Color32.white){
color = Color32.black;
}
String label = generateAtomLabel(atom);
double zoff = atom.getBiggestDisplayedRadius();
renderer.drawString(atom.x, atom.y, atom.z,
zoff,
color, label);
}
String format = atom.getCustomLabel();
if(format != null){
int color = Color32.white;
if(renderer.getBackgroundColor() ==
Color32.white){
color = Color32.black;
}
String customLabel = atom.generateLabel(format);
double zoff = atom.getBiggestDisplayedRadius();
renderer.drawString(atom.x, atom.y, atom.z,
zoff,
color,
customLabel);
}
if(atom.isSelected()){
//Log.info("atom selected " + atom);
renderer.drawBox(atom.xs, atom.ys, atom.zs,
boxPixels, Color32.yellow);
}
}
}
}
}
}
}
}
}
if(!sphereAtoms.isEmpty()){
for(Atom satom : sphereAtoms){
renderer.drawSphere(satom.x, satom.y, satom.z,
satom.getVDWRadius(), satom.getSelectedColor(), satom.getTransparency());
}
}
}
private Molecule currentMolecule = null;
/** Draw the molecule. */
private void drawMolecules(){
transformMolecule();
for(Molecule molecule : molecules){
boolean displayHydrogens = molecule.getBoolean(Molecule.DisplayHydrogens, false);
if(molecule.getDisplayed()){
int style = molecule.getDisplayStyle();
if((style & Molecule.Normal) == Molecule.Normal){
currentMolecule = molecule;
if(allowFastDraw){
fastDraw = !molecule.getBoolean(Molecule.DisplayBondDetails, true);
}else{
fastDraw = true;
}
int chainCount = molecule.getChainCount();
for(int c = 0; c < chainCount; c++){
Chain chain = molecule.getChain(c);
int residueCount = chain.getResidueCount();
for(int r = 0; r < residueCount; r++){
Residue res = chain.getResidue(r);
int atomCount = res.getAtomCount();
for(int a = 0; a < atomCount; a++){
Atom atom = res.getAtom(a);
int bondCount = atom.getBondCount();
for(int b = 0; b < bondCount; b++){
Bond bond = atom.getBond(b);
Atom firstAtom = bond.getFirstAtom();
if((displayHydrogens ||
firstAtom.getElement() != PeriodicTable.HYDROGEN) &&
atom == firstAtom){
Atom secondAtom = bond.getSecondAtom();
if(displayHydrogens ||
secondAtom.getElement() != PeriodicTable.HYDROGEN){
if(firstAtom.isSimpleDisplayed() &&
secondAtom.isSimpleDisplayed()){
double w = -bond.getBondWidth();
drawBond(bond, w);
}
if(firstAtom.attributes.contains(Atom.Attribute.Cylinder) &&
secondAtom.attributes.contains(Atom.Attribute.Cylinder)){
double w = bond.getCylinderWidth();
drawBond(bond, w);
}
if(firstAtom.attributes.contains(Atom.Attribute.BallAndStick) &&
secondAtom.attributes.contains(Atom.Attribute.BallAndStick)){
double w = bond.getStickWidth();
drawSimpleBond(bond, w);
}
}
}
}
}
}
}
}
if((style & Molecule.Trace) == Molecule.Trace){
drawTrace(molecule);
}
}
}
drawBumpPairs();
drawDistances();
drawAngles();
drawTorsions();
drawHbonds();
}
Atom atoms[] = new Atom[4];
private Point3d ta01 = new Point3d();
private Point3d ta12 = new Point3d();
private Point3d ta23 = new Point3d();
private Point3d n012 = new Point3d();
private Point3d n123 = new Point3d();
private Point3d p01 = new Point3d();
private Point3d p23 = new Point3d();
private Point3d m12 = new Point3d();
private Point3d n = new Point3d();
private Point3d last = new Point3d();
private Point3d arc = new Point3d();
private void drawTorsion(Point3d a0, Point3d a1,
Point3d a2, Point3d a3){
Point3d.unitVector(ta01, a0, a1);
Point3d.unitVector(ta12, a1, a2);
Point3d.unitVector(ta23, a2, a3);
Point3d.cross(n012, ta01, ta12);
Point3d.cross(n123, ta12, ta23);
Point3d.cross(p01, n012, ta12);
Point3d.cross(p23, n123, ta12);
Point3d.mid(m12, a1, a2);
// second orthogonal vector
Point3d.cross(n, ta12, p01);
double radius = 0.3;
int lineColor = Color32.magenta;
double lineRadius = 0.015;
{
double dot = radius / p01.dot(ta01);
renderer.drawCylinder(m12.x + radius * p01.x,
m12.y + radius * p01.y,
m12.z + radius * p01.z,
a1.x + dot * ta01.x,
a1.y + dot * ta01.y,
a1.z + dot * ta01.z,
lineColor, lineColor, lineRadius);
}
// second part
{
double dot = radius/p23.dot(ta23);
renderer.drawCylinder(m12.x + radius * p23.x,
m12.y + radius * p23.y,
m12.z + radius * p23.z,
a2.x + dot * ta23.x,
a2.y + dot * ta23.y,
a2.z + dot * ta23.z,
lineColor, lineColor, lineRadius);
}
// torsion angle
double t = Point3d.torsion(a0, a1, a2, a3);
// arc
double step = 5.0;
step *= (Math.PI/180.0);
if(t < 0.0){
step = -step;
}
int steps = 1 + (int)((t/step) + 0.5);
double angle = 0.0;
for(int s = 0; s < steps; s++){
double ct = Math.cos(angle);
double st = Math.sin(angle);
arc.x = m12.x + radius * (ct * p01.x + st * n.x);
arc.y = m12.y + radius * (ct * p01.y + st * n.y);
arc.z = m12.z + radius * (ct * p01.z + st * n.z);
if(s > 0){
renderer.drawCylinder(arc.x, arc.y, arc.z,
last.x, last.y, last.z,
lineColor, lineColor, lineRadius);
}
last.set(arc);
angle += step;
}
String format = String.format("%.1f", t * 180.0/Math.PI);
renderer.drawString(m12.x, m12.y, m12.z, 1.0, Color32.white, format);
}
/** Draw the molecule as a trace. */
private void drawTrace(Molecule molecule){
int atomCount = molecule.getAtomCount();
Atom previous = null;
for(int i = 0; i < atomCount; i++){
Atom a = molecule.getAtom(i);
if("CA".equals(a.getAtomLabel())){
if(previous != null && previous.distance(a) < 4.1 &&
previous.isDisplayed() && a.isDisplayed()){
int previousColor = previous.getColor();
int atomColor = a.getColor();
int width = 2;
if(previousColor == atomColor){
drawLine(previous.xs, previous.ys, previous.zs,
a.xs, a.ys, a.zs,
previousColor, previousColor, -width);
}else{
drawLine(previous.xs, previous.ys, a.zs,
a.xs, a.ys, a.zs,
previousColor, atomColor,
-width);
}
}
previous = a;
}
}
}
/** Draw the distance markers. */
private void drawDistances(){
if(displayDistances){
for(Distance distance : distances){
drawDistanceObject(distance);
}
}
}
/**
* Draw the specific distance.
* The dash on and off values are adjusted
* to give a half off gap at each end of the
* line.
*/
private void drawDistanceObject(Distance distance){
if(!distance.getBoolean(Distance.Visible, true)) return;
if(distance.getInteger(Distance.Mode, -1) == Distance.Centroids){
if(distance.valid() && !distance.group0.isEmpty()){
Point3d g0 = distance.getCenter0();
Point3d g1 = distance.getCenter1();
drawDashedLine(g0.x, g0.y, g0.z,
g1.x, g1.y, g1.z,
distance.getDouble(Distance.On, 0.2),
distance.getDouble(Distance.Off, 0.2),
distance.getDouble(Distance.Radius, -1.0),
((Color)distance.get(Distance.Color, Color.white)).getRGB());
drawDistanceMarker(g0, g1, distance);
}
}else{
for(Iterator<Point3d> it1 = distance.group0.iterator(), it2 = distance.group1.iterator(); it1.hasNext() && it2.hasNext();){
Atom g0 = (Atom)it1.next();
Atom g1 = (Atom)it2.next();
if(g0.isDisplayed() && g1.isDisplayed()){
drawDashedLine(g0.x, g0.y, g0.z,
g1.x, g1.y, g1.z,
distance.getDouble(Distance.On, 0.2),
distance.getDouble(Distance.Off, 0.2),
distance.getDouble(Distance.Radius, -1.0),
((Color)distance.get(Distance.Color, Color.white)).getRGB());
drawDistanceMarker(g0, g1, distance);
}
}
}
}
/** Space for the dashed line drawer. */
private Point3d dla = new Point3d();
private Point3d dlb = new Point3d();
/** Draw distance marker. */
private void drawDistanceMarker(Point3d g0, Point3d g1, Distance d){
String label = d.getString(Distance.Format, null);
if(label != null){
// if there is a format character
// calculate and subsitute the distance.
if(label.indexOf('%') != -1){
double len = g0.distance(g1);
label = String.format(label, len);
}
double mx = 0.5 * (g0.x + g1.x);
double my = 0.5 * (g0.y + g1.y);
double mz = 0.5 * (g0.z + g1.z);
renderer.drawString(mx, my, mz, 0.5,
((Color)d.get(Distance.LabelColor, Color.white)).getRGB(),
label);
}
}
/** Draw the distance marker line. */
private void drawDashedLine(double ax, double ay, double az,
double bx, double by, double bz,
double on, double off, double radius,
int color){
dla.set(ax, ay, az);
dlb.set(bx, by, bz);
Point3d v = Point3d.unitVector(dla, dlb);
double len = dla.distance(dlb);
double totalDash = on + off;
double scale = (len/totalDash)/(int)(len / totalDash);
on *= scale;
off *= scale;
double d = 0.5 * off;
if(Math.abs(off) < 1.e-3 || Math.abs(on) < 1.e-3){
drawLine(dla.x, dla.y, dla.z, dlb.x, dlb.y, dlb.z,
color, color, radius);
}else{
while((d + on) < (len)){
double dend = d + on;
drawLine(dla.x + d * v.x, dla.y + d * v.y, dla.z + d * v.z,
dla.x + dend * v.x, dla.y + dend * v.y, dla.z + dend * v.z,
color, color, radius);
d += on + off;
}
}
}
/** Format for angles. */
private Format angleFormat = new Format("%.1f");
/** Draw the angle markers. */
private void drawTorsions(){
int count = torsions.size();
for(int i = 0; i < count; i += 4){
boolean drawTorsion = true;
// make sure that we don't draw torsions
// when the atoms aren't displayed
for(int j = 0; j < 4; j++){
Atom aa = torsions.get(i + j);
Molecule mol = aa.getMolecule();
if(!aa.isDisplayed() || !mol.getDisplayed()){
drawTorsion = false;
}
}
if(drawTorsion){
Atom a1 = torsions.get(i);
Atom a2 = torsions.get(i + 1);
Atom a3 = torsions.get(i + 2);
Atom a4 = torsions.get(i + 3);
drawTorsion(a1, a2, a3, a4);
}
}
}
/** Draw the angle markers. */
private void drawAngles(){
int count = angles.size();
for(int i = 0; i < count; i += 3){
boolean drawAngle = true;
// make sure that we don't draw angles
// when the atoms aren't displayed
for(int j = 0; j < 3; j++){
Atom aa = angles.get(i + j);
Molecule mol = aa.getMolecule();
if(!aa.isDisplayed() || !mol.getDisplayed()){
drawAngle = false;
}
}
if(drawAngle){
Atom a1 = angles.get(i);
Atom a2 = angles.get(i + 1);
Atom a3 = angles.get(i + 2);
drawAngle(a1, a2, a3);
}
}
}
/**
* Draw an angle marker.
* Very simple initial implementation.
*/
private void drawAngle(Atom a1, Atom a2, Atom a3){
double xm = (a1.x + a2.x + a3.x)/3.0;
double ym = (a1.y + a2.y + a3.y)/3.0;
double zm = (a1.z + a2.z + a3.z)/3.0;
double angle = Point3d.angleDegrees(a1, a2, a3);
String label = angleFormat.format(angle);
renderer.drawString(xm, ym, zm, Color32.yellow, label);
}
/** Draw the bump atoms. */
private void drawBumpPairs(){
for(Iterator<Atom> it = bumpAtoms.iterator(); it.hasNext(); ){
Atom atom1 = it.next();
Atom atom2 = it.next();
drawDistance(atom1, atom2, true);
}
}
/** Draw hydrogen bonds. */
private void drawHbonds(){
for(Bond hbond : hbonds){
Atom atom0 = hbond.getAtom(0);
Atom atom1 = hbond.getAtom(1);
if(atom0.isDisplayed() && atom1.isDisplayed()){
drawDistance(atom0, atom1, false);
}
}
}
/** Format for distances. */
private Format distanceFormat = new Format("%.2fA");
/** Draw a distance between two atoms. */
private void drawDistance(Atom atom1, Atom atom2, boolean displayDistance){
Molecule molecule1 = atom1.getMolecule();
Molecule molecule2 = atom2.getMolecule();
if(molecule1.getDisplayStyle(Molecule.Normal) &&
molecule2.getDisplayStyle(Molecule.Normal) &&
molecule1.getDisplayed() &&
molecule2.getDisplayed() &&
atom1.isDisplayed() && atom2.isDisplayed()){
drawDottedLine(atom1, atom2, 0.2, Color32.white);
if(displayDistance){
double xm = (atom1.x + atom2.x)/2;
double ym = (atom1.y + atom2.y)/2;
double zm = (atom1.z + atom2.z)/2;
double d = atom1.distance(atom2);
String label = distanceFormat.format(d);
renderer.drawString(xm, ym, zm, Color32.white, label);
}
}
}
/** Dummy Atom for transforming dot points. */
private Atom dummyAtom = Atom.create();
/** Draw a dotted line. */
private void drawDottedLine(Atom atom1, Atom atom2,
double gap, int color){
double d = atom1.distance(atom2);
double current = gap;
Point3d v12 = Point3d.unitVector(atom1, atom2);
while(current < d){
dummyAtom.set(atom1.x + v12.x * current,
atom1.y + v12.y * current,
atom1.z + v12.z * current);
dummyAtom.transformToScreen(renderer.overallMatrix);
renderer.drawDot(dummyAtom.xs, dummyAtom.ys, dummyAtom.zs, color);
current += gap;
}
}
/** Draw a dotted line. */
private void drawTwinColourDottedLine(Atom atom1, Atom atom2,
double gap){
double d = atom1.distance(atom2);
double current = gap;
Point3d v12 = Point3d.unitVector(atom1, atom2);
while(current < d){
dummyAtom.set(atom1.x + v12.x * current,
atom1.y + v12.y * current,
atom1.z + v12.z * current);
dummyAtom.transformToScreen(renderer.overallMatrix);
//int shade = getShade(color, dummyAtom.zs);
int shade = 0;
renderer.setPixel(dummyAtom.xs, dummyAtom.ys, dummyAtom.zs, shade);
current += gap;
}
}
/** Size of box for selected atoms in Angstroms. */
private static final double boxSize = 0.05;
/** Default format for short atom labels. */
private static String defaultShortFormat = null;
/** Generate the atom label. */
private String generateAtomLabel(Atom atom){
if(defaultShortFormat == null){
defaultShortFormat = Settings.getString("config", "atom.short.format");
if(defaultShortFormat == null){
// no config value here
defaultShortFormat = "%a %R:%c%r%I";
}
}
return atom.generateLabel(defaultShortFormat);
}
/** Generate an atom label according to the format statments. */
public void generateAtomLabels(String format, List<Atom> selectedAtoms){
for(Atom a : selectedAtoms){
a.setCustomLabel(format);
}
}
/** Draw one atom. */
private void drawAtom(Atom atom, int crossPixels){
if(displaySolvent || !atom.isSolvent()){
int z = atom.zs;
int atomColor = atom.getColor();
if(crossPixels == 1){
renderer.drawDot(atom.xs, atom.ys, z, atomColor);
}else{
crossPixels <<= Renderer.FixedBits;
crossPixels /= 2;
int x = atom.xs;
int y = atom.ys;
drawLine(x - crossPixels, y, z,
x + crossPixels, y, z, atomColor, atomColor, -1);
drawLine(x, y - crossPixels, z,
x, y + crossPixels, z, atomColor, atomColor, -1);
}
}
}
/** Draw one bond. */
private void drawBond(Bond bond, double w){
if(fastDraw || bond.getBondOrder() == Bond.BondOrder.SingleBond){
drawSimpleBond(bond, w);
}else{
drawDetailedBond(bond, w);
}
}
/** Set whether or not we display bond types. */
public void displayBondTypes(boolean b){
allowFastDraw = b;
}
/** Draw a bond that shows the bond order. */
private void drawDetailedBond(Bond bond, double w){
Bond.BondOrder bondOrder = bond.getBondOrder();
if(bondOrder == Bond.BondOrder.DoubleBond ||
bondOrder == Bond.BondOrder.AromaticBond){
drawDoubleBond(bond, false, w > 0.0 ? w * doubleBondRadiusScale : w);
drawSimpleBond(bond, w);
}else if(bondOrder == Bond.BondOrder.TripleBond){
drawDoubleBond(bond, true, w > 0.0 ? w * doubleBondRadiusScale : w);
drawSimpleBond(bond, w);
}else{
drawSimpleBond(bond, w);
}
}
private double doubleBondOffset = 0.35;
private double doubleBondRadiusScale = 0.4;
private Atom dummyAtom1 = Atom.create();
private Atom dummyAtom2 = Atom.create();
private double aromaticBondDotGap = 0.2;
/** Draw a double bond. */
private void drawDoubleBond(Bond bond, boolean triple, double w){
Atom firstAtom = bond.getAtom(0);
Atom secondAtom = bond.getAtom(1);
int firstAtomColor = firstAtom.getColor();
int secondAtomColor = secondAtom.getColor();
Ring ring = currentMolecule.getBestRingContainingBond(bond);
if(ring != null){
Point3d center = ring.getRingCenter();
Point3d mid = new Point3d();
mid.add(firstAtom);
mid.add(secondAtom);
mid.scale(0.5);
Point3d mid2Center = Point3d.vector(mid, center);
double length = mid2Center.length();
double scale = doubleBondOffset / length;
Point3d first2Center = Point3d.vector(firstAtom, center);
Point3d second2Center = Point3d.vector(secondAtom, center);
first2Center.scale(scale);
second2Center.scale(scale);
first2Center.add(firstAtom);
second2Center.add(secondAtom);
if(bond.getBondOrder() == Bond.BondOrder.AromaticBond){
dummyAtom1.set(first2Center);
dummyAtom2.set(second2Center);
drawTwinColourDottedLine(dummyAtom1, dummyAtom2, aromaticBondDotGap);
}else{
drawLine(first2Center.x, first2Center.y, first2Center.z,
second2Center.x, second2Center.y, second2Center.z,
firstAtomColor, secondAtomColor, w);
}
}else{
Point3d first2second = Point3d.unitVector(firstAtom, secondAtom);
first2second.normalize();
Point3d normal = getDoubleBondOffsetVector(firstAtom, secondAtom);
normal.scale(doubleBondOffset);
first2second.scale(0.1);
Point3d firstEnd = firstAtom.clone();
firstEnd.add(normal);
if(firstAtom.getBondCount() > 1){
firstEnd.add(first2second);
}
Point3d secondEnd = secondAtom.clone();
secondEnd.add(normal);
if(secondAtom.getBondCount() > 1){
secondEnd.sub(first2second);
}
if(bond.getBondOrder() == Bond.BondOrder.AromaticBond){
dummyAtom1.set(firstEnd);
dummyAtom2.set(secondEnd);
drawTwinColourDottedLine(dummyAtom1, dummyAtom2, aromaticBondDotGap);
}else{
drawLine(firstEnd.x, firstEnd.y, firstEnd.z,
secondEnd.x, secondEnd.y, secondEnd.z,
firstAtomColor, secondAtomColor, w);
}
if(triple){
firstEnd = firstAtom.clone();
firstEnd.sub(normal);
if(firstAtom.getBondCount() > 1){
firstEnd.add(first2second);
}
secondEnd = secondAtom.clone();
secondEnd.sub(normal);
if(secondAtom.getBondCount() > 1){
secondEnd.sub(first2second);
}
drawLine(firstEnd.x, firstEnd.y, firstEnd.z,
secondEnd.x, secondEnd.y, secondEnd.z,
firstAtomColor, secondAtomColor, w);
}
}
}
/** Direction of double bond offset for non-ring double bond. */
private Point3d getDoubleBondOffsetVector(Atom firstAtom, Atom secondAtom){
Point3d first2second = Point3d.unitVector(firstAtom, secondAtom);
first2second.normalize();
Atom targetAtom = null;
Atom otherAtom = null;
if(firstAtom.getBondCount() > 1){
targetAtom = firstAtom;
otherAtom = secondAtom;
}else if(secondAtom.getBondCount() > 1){
targetAtom = secondAtom;
otherAtom = firstAtom;
}
if(targetAtom != null){
// now find a relevant bond.
int bondCount = targetAtom.getBondCount();
for(int i = 0; i < bondCount;i++){
Atom a = targetAtom.getBondedAtom(i);
if(a != otherAtom){
// this will do for the vector.
Point3d dir = Point3d.unitVector(targetAtom, a);
Point3d reference = dir.cross(first2second);
reference.normalize();
Point3d normal = first2second.cross(reference);
normal.normalize();
return normal;
}
}
}
return Point3d.normalToLine(first2second);
}
private double bondLineRadius = -1.0;
/** Draw a simple bond just made up of a colored line. */
private void drawSimpleBond(Bond bond, double w){
Atom firstAtom = bond.getAtom(0);
Atom secondAtom = bond.getAtom(1);
int firstAtomColor = firstAtom.getColor();
int secondAtomColor = secondAtom.getColor();
if(w < 0.0){
if(renderer.shadowMode != Renderer.ShadowMode.ShadowsOff){
drawLine(firstAtom.x, firstAtom.y, firstAtom.z,
secondAtom.x, secondAtom.y, secondAtom.z,
firstAtomColor, secondAtomColor, (-w * bondLineRadius));
}else{
drawLine(firstAtom.xs, firstAtom.ys, firstAtom.zs,
secondAtom.xs, secondAtom.ys, secondAtom.zs,
firstAtomColor, secondAtomColor, w);
}
}else{
if(firstAtom.isSelected()){
firstAtomColor = Color32.yellow;
}
if(secondAtom.isSelected()){
secondAtomColor = Color32.yellow;
}
drawLine(firstAtom.x, firstAtom.y, firstAtom.z,
secondAtom.x, secondAtom.y, secondAtom.z,
firstAtomColor, secondAtomColor, w);
}
}
/** Entry point for line/cylinder drawing. */
private void drawLine(int x1, int y1, int z1,
int x2, int y2, int z2,
int rgb1, int rgb2, double width){
if(width < 0.0){
int iw = (int)(-width + 0.5);
renderer.drawLine(x1, y1, z1, x2, y2, z2, rgb1, rgb2, iw);
}else{
renderer.drawCylinder(x1, y1, z1, x2, y2, z2, rgb1, rgb2, width);
}
}
/** Entry point for line/cylinder drawing. */
private void drawLine(double x1, double y1, double z1,
double x2, double y2, double z2,
int rgb1, int rgb2, double width){
if(width < 0.0){
int iw = (int)(-width + 0.5);
renderer.drawLine(x1, y1, z1, x2, y2, z2, rgb1, rgb2, iw);
}else{
renderer.drawCylinder(x1, y1, z1, x2, y2, z2, rgb1, rgb2, width);
}
}
/** Find atom with screen coordinates nearest to the specified point. */
public Atom getNearestAtom(int x, int y){
Atom nearestAtom = null;
int nearest = Integer.MAX_VALUE;
for(Molecule molecule : molecules){
int atomCount = molecule.getAtomCount();
int style = molecule.getDisplayStyle();
if((style & Molecule.Normal) == Molecule.Normal &&
molecule.getDisplayed()){
for(int a = 0; a < atomCount; a++){
Atom atom = molecule.getAtom(a);
int az = atom.zs;
if(atom.isDisplayed() &&
az >= renderer.backClip && az <= renderer.frontClip){
int ax = atom.xs >> Renderer.FixedBits;
int ay = atom.ys >> Renderer.FixedBits;
int dSquare = (ax - x)*(ax - x) + (ay - y)*(ay - y);
if(dSquare < nearest && dSquare < 64){
nearest = dSquare;
nearestAtom = atom;
}
}
}
}else if((style & Molecule.Trace) == Molecule.Trace &&
molecule.getDisplayed()){
for(int a = 0; a < atomCount; a++){
Atom atom = molecule.getAtom(a);
int az = atom.zs;
if(atom.isDisplayed() &&
"CA".equals(atom.getAtomLabel()) &&
az >= renderer.backClip && az <= renderer.frontClip){
int ax = atom.xs >> Renderer.FixedBits;
int ay = atom.ys >> Renderer.FixedBits;
int dSquare = (ax - x)*(ax - x) + (ay - y)*(ay - y);
if(dSquare < nearest && dSquare < 64){
nearest = dSquare;
nearestAtom = atom;
}
}
}
}
}
return nearestAtom;
}
/** Translate the center of the view. */
public void translateCenter(int dx, int dy){
Point3d direction = new Point3d(-dx, dy, 0);
renderer.rotationMatrix.transformByInverse(direction);
direction.divide(renderer.getOverallScale());
Point3d center = renderer.getCenter();
center.add(direction);
renderer.setCenter(center);
}
/* Various methods for applying color schemes to molecules. */
/** List of color names for the different chains. */
private static int chainColors[] = {
Color32.green,
Color32.yellow,
Color32.orange,
Color32.blue,
Color32.red,
};
/** Color by chain. */
public void colorByChain(){
boolean all = (getSelectedAtomCount() == 0);
int chainNumber = 0;
for(Molecule molecule : molecules){
int chainCount = molecule.getChainCount();
for(int c = 0; c < chainCount; c++){
Chain chain = molecule.getChain(c);
int residueCount = chain.getResidueCount();
int color = chainColors[chainNumber % chainColors.length];
for(int r = 0; r < residueCount; r++){
Residue residue = chain.getResidue(r);
int atomCount = residue.getAtomCount();
for(int a = 0; a < atomCount; a++){
Atom atom = residue.getAtom(a);
if(all || atom.isSelected()){
atom.setColor(color);
}
}
}
chainNumber++;
}
}
}
/** Color by atom type. */
public void colorByAtom(){
boolean all = (getSelectedAtomCount() == 0);
for(Molecule molecule : molecules){
int atomCount = molecule.getAtomCount();
boolean symmetryMolecule = molecule.isSymmetryMolecule();
for(int a = 0; a < atomCount; a++){
Atom atom = molecule.getAtom(a);
if(all || atom.isSelected()){
atom.resetColor();
int color = atom.getColor();
if(symmetryMolecule &&
atom.getElement() == PeriodicTable.CARBON){
color = Color32.grey;
}
atom.setColor(color);
}
}
}
}
/**
* Color by bFactor.
* This method uses a fixed range to set the b-factor
* colors as the absolute value has meaning.
*/
public void colorByBFactor(){
boolean all = (getSelectedAtomCount() == 0);
AtomIterator atomIterator = getAtomIterator();
while(atomIterator.hasMoreElements()){
Atom atom = atomIterator.getNextAtom();
if(all || atom.isSelected()){
double b = atom.getBFactor();
int color = 0;
if(b <= 10.0){
color = Color32.rwb7;
}else if(b > 10.0 && b <= 15.0){
color = Color32.rwb6;
}else if(b > 15.0 && b <= 20.0){
color = Color32.rwb5;
}else if(b > 20.0 && b <= 25.0){
color = Color32.rwb4;
}else if(b > 25.0 && b <= 30.0){
color = Color32.rwb3;
}else if(b > 30.0 && b <= 35.0){
color = Color32.rwb2;
}else if(b > 35.0 && b <= 40.0){
color = Color32.rwb1;
}else{
color = Color32.rwb0;
}
atom.setColor(color);
}
}
}
/**
* The colors that we will use for b-factor ranges.
*/
private static int predefinedColors[] = null;
private static final int ColorRampSize = 512;
private void ensureColorRampDefined(){
if(predefinedColors == null){
predefinedColors = new int[ColorRampSize];
int ColorRampSize2 = ColorRampSize/2;
for(int i = 0; i < ColorRampSize2; i++){
int r = i;
int g = i;
int b = 255;
predefinedColors[i] = Color32.pack(r, g, b);
}
for(int i = 0; i < ColorRampSize2; i++){
int r = 255;
int g = 255 - i;
int b = 255 - i;
predefinedColors[ColorRampSize2 + i] =
Color32.pack(r, g, b);
}
}
}
/**
* Color by b-factor but bin the colors according to range.
*/
public void colorByPropertyRange(int property){
double min = 1.e10, max = -1.e10;
boolean all = (getSelectedAtomCount() == 0);
ensureColorRampDefined();
AtomIterator atomIterator = getAtomIterator();
double rms = 0.0;
int selectedCount = 0;
while(atomIterator.hasMoreElements()){
Atom atom = atomIterator.getNextAtom();
if(all || atom.isSelected()){
selectedCount++;
double b = atom.getAttribute(property);
rms += b * b;
if(b < min){
min = b;
}
if(b > max){
max = b;
}
}
}
if(selectedCount > 0){
rms /= (double)selectedCount;
rms = Math.sqrt(rms);
}
if(property == Atom.B){
max = 2. * rms;
}
atomIterator = getAtomIterator();
int colorCount = predefinedColors.length;
while(atomIterator.hasMoreElements()){
Atom atom = atomIterator.getNextAtom();
if(all || atom.isSelected()){
double b = atom.getAttribute(property);
int color = Color32.rwb7;
if(property == Atom.B && Math.abs(b) > 1.e-3){
color = (int)(colorCount * (b - min) / (max - min));
if(color < 0){
color = 0;
}else if(color >= colorCount){
color = colorCount - 1;
}
color = predefinedColors[color];
}
atom.setColor(color);
}
}
}
/** Working space for the rainbow coloring. */
private double hsvtmp[] = new double[3];
/** Max hue to stop at blue. */
private static final double maxHue = 240.0;
/**
* Color a set of atoms with rainbow hue.
* Rainbow goes from blue to red.
*/
public void colorByRainbow(List<Atom> selectedAtoms){
int atomCount = selectedAtoms.size();
double step = maxHue/(atomCount - 1);
double hue = maxHue;
hsvtmp[1] = 1.0;
hsvtmp[2] = 1.0;
for(Atom a : selectedAtoms){
hsvtmp[0] = hue;
int c = Color32.hsv2packed(hsvtmp);
a.setColor(c);
hue -= step;
}
}
/** The slide number for running slide shows. */
private int slideNumber = -1;
/** The slideshow we are showing. */
private String slideShow = null;
/** Handle a slide command. */
public void handleSlideCommand(Arguments args){
String slideShowString = args.getString("-slideshow", null);
int forward = args.getInteger("-forward", Integer.MIN_VALUE);
int backward = args.getInteger("-backward", Integer.MIN_VALUE);
int show = args.getInteger("-show", Integer.MIN_VALUE);
if(slideShowString != null){
slideShow = slideShowString;
slideNumber = 0;
Log.info("slideshow is " + slideShow);
}
if(forward != Integer.MIN_VALUE){
slideNumber += forward;
}
if(backward != Integer.MIN_VALUE){
slideNumber -= backward;
}
if(show != Integer.MIN_VALUE){
slideNumber = show;
}
// only go back to -1
if(slideNumber < 0){
slideNumber = 0;
}
Log.info("showing slide %d", slideNumber);
if(slideShow != null){
String script = String.format(slideShow, slideNumber);
Log.info("about to play slide " + script);
executeScript(script);
}else{
Log.error("no slideshow defined");
}
}
/** The buffer of commands we executed. */
transient StringBuffer commandLog = new StringBuffer(65536);
public synchronized void execute(String command){
executeInternal(command);
}
private transient parser parserStack[] = new parser[10];
private transient Yylex lexerStack[] = new Yylex[10];
private int parserDepth = -1;
private synchronized boolean parse(java.io.Reader reader){
boolean errorCondition = false;
if(parserStack == null) parserStack = new parser[10];
if(lexerStack == null) lexerStack = new Yylex[10];
parserDepth++;
try {
// only allocate the parser and lexer for each
// depth when needed, the lexer in particular
// uses quite a lot of memory...
if(parserStack[parserDepth] == null){
parserStack[parserDepth] = new parser();
lexerStack[parserDepth] =
new Yylex((java.io.BufferedReader)null);
parserStack[parserDepth].setScanner(lexerStack[parserDepth]);
}
parserStack[parserDepth].setMoleculeRenderer(this);
lexerStack[parserDepth].setInput(reader);
parserStack[parserDepth].parse();
}catch(Exception e){
//Log.error("error parsing: " + command);
e.printStackTrace();
errorCondition = true;
}
parserDepth--;
return (!errorCondition);
}
private synchronized void executeInternal(String command){
StringReader sr = new StringReader(command);
if(!parse(sr)){
System.err.println("Syntax error in command:");
System.err.println(command);
}else{
if(commandLog == null){
commandLog = new StringBuffer(command.length() * 3);
}
commandLog.append(command);
}
sr.close();
}
/** The last script we executed. */
private String lastScriptFile = null;
public synchronized void reExecute(){
if(lastScriptFile != null){
System.out.println("reExecute " + lastScriptFile);
executeScript(lastScriptFile);
}
}
/** Execute a script of commands. */
public synchronized void executeScript(String filename){
try {
boolean previous = FILE.getTryFiles();
FILE.setTryFiles(true);
FILE file = FILE.open(filename);
if(file != null){
InputStream is = file.getInputStream();
parse(new InputStreamReader(is));
file.close();
lastScriptFile = filename;
}else{
System.out.println("couldn't open script " + filename);
lastScriptFile = null;
}
file = FILE.open(filename);
if(file != null){
while(file.nextLine()){
String line = file.getCurrentLineAsString();
commandLog.append(line);
}
file.close();
}
FILE.setTryFiles(previous);
}catch(Exception e){
System.out.println("error processing command: ");
e.printStackTrace();
}
}
}