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