package jalview.ext.pymol;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import jalview.structure.AtomSpecModel;
import jalview.structure.StructureCommand;
import jalview.structure.StructureCommandI;
import jalview.structure.StructureCommandsBase;
/**
* A class that generates commands to send to PyMol over its XML-RPC interface.
*
* Note that because the xml-rpc interface can only accept one command at a
* time, we can't concatenate commands, and must instead form and send them
* individually.
*
* @see https://pymolwiki.org/index.php/Category:Commands
* @see https://pymolwiki.org/index.php/RPC
*/
public class PymolCommands extends StructureCommandsBase
{
// https://pymol.org/dokuwiki/doku.php?id=command:zoom
// not currently documented on
// https://pymolwiki.org/index.php/Category:Commands
private static final StructureCommand FOCUS_VIEW = new StructureCommand(
"zoom");
// https://pymolwiki.org/index.php/Quit
private static final StructureCommand CLOSE_PYMOL = new StructureCommand(
"quit");
// not currently documented on
// https://pymolwiki.org/index.php/Category:Commands
private static final StructureCommand COLOUR_BY_CHAIN = new StructureCommand(
"spectrum", "chain");
private static final List COLOR_BY_CHARGE = Arrays
.asList(new StructureCommand("color", "white", "*"),
new StructureCommand("color", "red", "resn ASP resn GLU"),
new StructureCommand("color", "blue",
"resn LYS resn ARG"),
new StructureCommand("color", "yellow", "resn CYS"));
private static final List SHOW_BACKBONE = Arrays
.asList(new StructureCommand("hide", "everything"),
new StructureCommand("show", "ribbon"));
@Override
public StructureCommandI colourByChain()
{
return COLOUR_BY_CHAIN;
}
@Override
public List colourByCharge()
{
return COLOR_BY_CHARGE;
}
@Override
public StructureCommandI setBackgroundColour(Color col)
{
// https://pymolwiki.org/index.php/Bg_Color
return new StructureCommand("bg_color", getColourString(col));
}
/**
* Returns a colour formatted suitable for use in viewer command syntax. For
* example, red is {@code "0xff0000"}.
*
* @param c
* @return
*/
protected String getColourString(Color c)
{
return String.format("0x%02x%02x%02x", c.getRed(), c.getGreen(),
c.getBlue());
}
@Override
public StructureCommandI focusView()
{
return FOCUS_VIEW;
}
@Override
public List showChains(List toShow)
{
// https://pymolwiki.org/index.php/Show
List commands = new ArrayList<>();
commands.add(new StructureCommand("hide", "everything"));
commands.add(new StructureCommand("show", "lines"));
StringBuilder chains = new StringBuilder();
for (String chain : toShow)
{
chains.append(" chain ").append(chain);
}
commands.add(
new StructureCommand("show", "cartoon", chains.toString()));
return commands;
}
@Override
public List superposeStructures(AtomSpecModel refAtoms,
AtomSpecModel atomSpec)
{
// https://pymolwiki.org/index.php/Super
List commands = new ArrayList<>();
String refAtomsAlphaOnly = getAtomSpec(refAtoms, true);
String atomSpec2AlphaOnly = getAtomSpec(atomSpec, true);
commands.add(new StructureCommand("super", refAtomsAlphaOnly,
atomSpec2AlphaOnly));
/*
* and show superposed residues as cartoon
*/
String refAtomsAll = getAtomSpec(refAtoms, false);
String atomSpec2All = getAtomSpec(atomSpec, false);
commands.add(new StructureCommand("show", "cartoon",
refAtomsAll + " " + atomSpec2All));
return commands;
}
@Override
public StructureCommandI openCommandFile(String path)
{
// https://pymolwiki.org/index.php/Run
return new StructureCommand("run", path); // should be .pml
}
@Override
public StructureCommandI saveSession(String filepath)
{
// https://pymolwiki.org/index.php/Save#EXAMPLES
return new StructureCommand("save", filepath); // should be .pse
}
/**
* Returns a selection string in PyMOL 'selection macro' format:
*
*
* modelId// chain/residues/
*
*
* If more than one chain, makes a selection expression for each, and they are
* separated by spaces.
*
* @see https://pymolwiki.org/index.php/Selection_Macros
*/
@Override
public String getAtomSpec(AtomSpecModel model, boolean alphaOnly)
{
StringBuilder sb = new StringBuilder(64);
boolean first = true;
for (String modelId : model.getModels())
{
for (String chain : model.getChains(modelId))
{
if (!first)
{
sb.append(" ");
}
first = false;
List rangeList = model.getRanges(modelId, chain);
chain = chain.trim();
sb.append(modelId).append("//").append(chain).append("/");
boolean firstRange = true;
for (int[] range : rangeList)
{
if (!firstRange)
{
sb.append("+");
}
firstRange = false;
sb.append(String.valueOf(range[0]));
if (range[0] != range[1])
{
sb.append("-").append(String.valueOf(range[1]));
}
}
sb.append("/");
if (alphaOnly)
{
sb.append("CA");
}
}
}
return sb.toString();
}
@Override
public List showBackbone()
{
return SHOW_BACKBONE;
}
@Override
protected StructureCommandI colourResidues(String atomSpec, Color colour)
{
// https://pymolwiki.org/index.php/Color
return new StructureCommand("color", getColourString(colour), atomSpec);
}
@Override
protected String getResidueSpec(String residue)
{
// https://pymolwiki.org/index.php/Selection_Algebra
return "resn " + residue;
}
@Override
public StructureCommandI loadFile(String file)
{
return new StructureCommand("load", file);
}
/**
* Overrides the default implementation (which generates concatenated
* commands) to generate one per colour (because the XML-RPC interface to
* PyMOL only accepts one command at a time)
*
* @param colourMap
* @return
*/
@Override
public List colourBySequence(
Map