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}