001/* 002 * BioJava development code 003 * 004 * This code may be freely distributed and modified under the 005 * terms of the GNU Lesser General Public Licence. This should 006 * be distributed with the code. If you do not have a copy, 007 * see: 008 * 009 * http://www.gnu.org/copyleft/lesser.html 010 * 011 * Copyright for this code is held jointly by the individual 012 * authors. These should be listed in @author doc comments. 013 * 014 * For more information on the BioJava project and its aims, 015 * or to join the biojava-l mailing list, visit the home page 016 * at: 017 * 018 * http://www.biojava.org/ 019 * 020 */ 021package org.biojava.nbio.structure.symmetry.gui; 022 023import java.awt.event.KeyEvent; 024import java.util.ArrayList; 025import java.util.Arrays; 026import java.util.List; 027 028import javax.swing.JMenu; 029import javax.swing.JMenuBar; 030import javax.swing.JMenuItem; 031 032import org.biojava.nbio.structure.Atom; 033import org.biojava.nbio.structure.Group; 034import org.biojava.nbio.structure.StructureException; 035import org.biojava.nbio.structure.StructureTools; 036import org.biojava.nbio.structure.align.gui.MultipleAlignmentJmolDisplay; 037import org.biojava.nbio.structure.align.gui.StructureAlignmentDisplay; 038import org.biojava.nbio.structure.align.gui.jmol.AbstractAlignmentJmol; 039import org.biojava.nbio.structure.align.gui.jmol.MultipleAlignmentJmol; 040import org.biojava.nbio.structure.align.multiple.MultipleAlignment; 041import org.biojava.nbio.structure.align.util.RotationAxis; 042import org.biojava.nbio.structure.symmetry.axis.AxisAligner; 043import org.biojava.nbio.structure.symmetry.core.QuatSymmetryResults; 044import org.biojava.nbio.structure.symmetry.internal.CeSymmResult; 045import org.biojava.nbio.structure.symmetry.internal.SymmetryAxes; 046import org.biojava.nbio.structure.symmetry.internal.SymmetryAxes.Axis; 047import org.biojava.nbio.structure.symmetry.jmolScript.JmolSymmetryScriptGenerator; 048import org.biojava.nbio.structure.symmetry.jmolScript.JmolSymmetryScriptGeneratorPointGroup; 049import org.biojava.nbio.structure.symmetry.utils.SymmetryTools; 050import org.slf4j.Logger; 051import org.slf4j.LoggerFactory; 052 053/** 054 * Class that provides visualizations methods for symmetry alignments. Call the 055 * display() method for the default visualization of symmetry. 056 * 057 * @author Aleix Lafita 058 * @since 4.2.0 059 * 060 */ 061public class SymmetryDisplay { 062 063 private static final Logger logger = LoggerFactory 064 .getLogger(SymmetryDisplay.class); 065 066 /** 067 * Displays a multiple alignment of the symmetry repeats. 068 * 069 * * @param symm CeSymmResult 070 * 071 * @throws StructureException 072 */ 073 public static MultipleAlignmentJmol displayRepeats(CeSymmResult symm) 074 throws StructureException { 075 076 MultipleAlignment repeats = SymmetryTools.toRepeatsAlignment(symm); 077 MultipleAlignmentJmol jmol = MultipleAlignmentJmolDisplay 078 .display(repeats); 079 jmol.setTitle(getSymmTitle(symm)); 080 return jmol; 081 } 082 083 /** 084 * Displays a multiple alignment of the whole structure transformations 085 * colored by blocks, corresponding to the symmetric protodomains. 086 * 087 * @param symm 088 * CeSymmResult 089 * @throws StructureException 090 */ 091 public static MultipleAlignmentJmol displayFull(CeSymmResult symm) 092 throws StructureException { 093 094 MultipleAlignment full = SymmetryTools.toFullAlignment(symm); 095 096 MultipleAlignmentJmol jmol = MultipleAlignmentJmolDisplay.display(full); 097 jmol.setColorByBlocks(true); 098 jmol.setTitle(getSymmTitle(symm)); 099 100 return jmol; 101 } 102 103 /** 104 * Displays a single structure in a cartoon representation with each 105 * symmetric repeat colored differently. 106 * 107 * @param msa 108 * the symmetry multiple alignment obtained from CeSymm 109 * @throws StructureException 110 */ 111 public static AbstractAlignmentJmol display(CeSymmResult symmResult) 112 throws StructureException { 113 114 if (symmResult.isSignificant() && symmResult.isRefined()) { 115 // Show the structure colored by repeat (do not rotate) 116 MultipleAlignment msa = symmResult.getMultipleAlignment(); 117 List<Atom[]> atoms = msa.getAtomArrays(); 118 119 // Add non polymer protein groups 120 Atom[] allAtoms = atoms.get(0); 121 List<Group> hetatms = StructureTools.getUnalignedGroups(allAtoms); 122 allAtoms = Arrays 123 .copyOf(allAtoms, allAtoms.length + hetatms.size()); 124 for (int h = 0; h < hetatms.size(); h++) { 125 int index = (allAtoms.length - hetatms.size()) + h; 126 allAtoms[index] = hetatms.get(h).getAtom(0); 127 } 128 for (int s = 0; s < msa.size(); s++) 129 atoms.set(s, allAtoms); 130 131 MultipleAlignmentJmol jmol = new MultipleAlignmentJmol(msa, atoms); 132 jmol.setTitle(jmol.getStructure().getPDBHeader().getTitle()); 133 addSymmetryMenu(jmol, symmResult); 134 jmol.evalString(printSymmetryGroup(symmResult)); 135 jmol.evalString(printSymmetryAxes(symmResult)); 136 jmol.setTitle(getSymmTitle(symmResult)); 137 jmol.evalString("save STATE state_1"); 138 return jmol; 139 } else { 140 // Show the optimal self-alignment 141 logger.info("Showing optimal self-alignment"); 142 Atom[] cloned = StructureTools 143 .cloneAtomArray(symmResult.getAtoms()); 144 AbstractAlignmentJmol jmol = StructureAlignmentDisplay.display( 145 symmResult.getSelfAlignment(), symmResult.getAtoms(), 146 cloned); 147 RotationAxis axis = new RotationAxis(symmResult.getSelfAlignment()); 148 jmol.evalString(axis.getJmolScript(symmResult.getAtoms())); 149 jmol.evalString("save STATE state_1"); 150 return jmol; 151 } 152 } 153 154 /** 155 * Adds a Symmetry menu to the Jmol display, so that further symmetry 156 * analysis can be triggered. 157 * 158 * @param jmol 159 * parent jmol 160 * @param symmResult 161 * CeSymmResult 162 */ 163 private static void addSymmetryMenu(MultipleAlignmentJmol jmol, 164 CeSymmResult symmResult) { 165 166 JMenuBar menubar = jmol.getFrame().getJMenuBar(); 167 168 JMenu symm = new JMenu("Symmetry"); 169 symm.setMnemonic(KeyEvent.VK_S); 170 171 SymmetryListener li = new SymmetryListener(jmol, symmResult); 172 173 JMenuItem repeats = new JMenuItem("Repeats Superposition"); 174 repeats.addActionListener(li); 175 symm.add(repeats); 176 177 JMenuItem multiple = new JMenuItem("Multiple Structure Alignment"); 178 multiple.addActionListener(li); 179 symm.add(multiple); 180 181 JMenuItem self = new JMenuItem("Optimal Self Alignment"); 182 self.addActionListener(li); 183 symm.add(self); 184 185 JMenuItem pg = new JMenuItem("Show Symmetry Group"); 186 pg.addActionListener(li); 187 symm.add(pg); 188 189 JMenuItem ax = new JMenuItem("Show Symmetry Axes"); 190 ax.addActionListener(li); 191 symm.add(ax); 192 193 JMenuItem news = new JMenuItem("New Symmetry Analysis"); 194 news.addActionListener(li); 195 symm.add(news); 196 197 menubar.add(symm, 3); 198 jmol.getFrame().pack(); 199 } 200 201 /** 202 * Generates a String that displays the symmetry axes of a structure. 203 * 204 * @param symm 205 * CeSymmResult 206 * @return 207 * @throws StructureException 208 */ 209 public static String printSymmetryAxes(CeSymmResult symm) 210 throws StructureException { 211 return printSymmetryAxes(symm,true); 212 } 213 214 /** 215 * Generates a String that displays the symmetry axes of a structure. 216 * 217 * @param symm 218 * CeSymmResult 219 * @param allAxes Indicates whether all axes should be displayed or just 220 * the elemenatary ones 221 * @return 222 * @throws StructureException 223 */ 224 public static String printSymmetryAxes(CeSymmResult symm,boolean allAxes) 225 throws StructureException { 226 227 int id = 0; 228 String script = ""; 229 SymmetryAxes axes = symm.getAxes(); 230 List<Atom[]> repeats = SymmetryTools.toRepeatsAlignment(symm) 231 .getAtomArrays(); 232 233 List<Axis> symmAxes; 234 if(allAxes) { 235 symmAxes = axes.getSymmetryAxes(); 236 } else { 237 symmAxes= axes.getElementaryAxesObjects(); 238 } 239 for (Axis a : symmAxes) { 240 RotationAxis rot = a.getRotationAxis(); 241 List<List<Integer>> cyclicForm = axes.getRepeatsCyclicForm(a); 242 List<Atom> repAtoms = new ArrayList<Atom>(); 243 for(List<Integer> cycle : cyclicForm) { 244 for(Integer repeat : cycle) { 245 repAtoms.addAll(Arrays.asList(repeats.get(repeat))); 246 } 247 } 248 249 script += rot.getJmolScript( 250 repAtoms.toArray(new Atom[repAtoms.size()]), id); 251 id++; 252 } 253 254 return script; 255 } 256 257 /** 258 * Given a symmetry alignment, it draws the symmetry group axes and the 259 * polyhedron box around the structure. It uses the quaternary symmetry 260 * detection code, but tries to factor out the alignment and detection 261 * steps. 262 * 263 * @param symm 264 * CeSymmResult 265 * @return 266 * @throws StructureException 267 */ 268 public static String printSymmetryGroup(CeSymmResult symm) 269 throws StructureException { 270 271 QuatSymmetryResults gSymmetry = SymmetryTools 272 .getQuaternarySymmetry(symm); 273 274 AxisAligner axes = AxisAligner.getInstance(gSymmetry); 275 276 // Draw the axes as in the quaternary symmetry 277 JmolSymmetryScriptGenerator scriptGenerator = JmolSymmetryScriptGeneratorPointGroup 278 .getInstance(axes, "g"); 279 280 String script = "save selection; set measurementUnits ANGSTROMS;" 281 + "select all; set antialiasDisplay true; autobond=false; "; 282 283 script += scriptGenerator.getInstantaneousOrientation(0); 284 script += "restore selection; "; 285 script += scriptGenerator.drawPolyhedron(); 286 script += scriptGenerator.drawAxes(); 287 script += "draw axes* on; draw poly* on; "; 288 289 return script; 290 } 291 292 /** 293 * Create a symmetry title for a display frame (Jmol, alignment, etc). The 294 * title contains information about the algorithm, structure id and 295 * parameters used. 296 * 297 * @param result 298 * @return title String 299 */ 300 public static String getSymmTitle(CeSymmResult result) { 301 302 StringBuffer buff = new StringBuffer(); 303 304 // Add algorithm name and version 305 buff.append(result.getMultipleAlignment().getEnsemble() 306 .getAlgorithmName()); 307 buff.append(" V"); 308 buff.append(result.getMultipleAlignment().getEnsemble().getVersion()); 309 buff.append(": "); 310 311 // Add the result summary string 312 buff.append(result.toString()); 313 return buff.toString(); 314 } 315 316}