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}