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 * Created on Oct 6, 2009
021 * Author: Andreas Prlic
022 *
023 */
024
025package org.biojava.nbio.structure.align.gui.jmol;
026
027import org.biojava.nbio.structure.*;
028import org.biojava.nbio.structure.align.gui.JPrintPanel;
029import org.biojava.nbio.structure.domain.LocalProteinDomainParser;
030import org.biojava.nbio.structure.domain.pdp.Domain;
031import org.biojava.nbio.structure.domain.pdp.Segment;
032import org.biojava.nbio.structure.gui.util.color.ColorUtils;
033import org.biojava.nbio.structure.jama.Matrix;
034import org.biojava.nbio.structure.scop.ScopDatabase;
035import org.biojava.nbio.structure.scop.ScopDomain;
036import org.biojava.nbio.structure.scop.ScopInstallation;
037import org.jmol.adapter.smarter.SmarterJmolAdapter;
038import org.jmol.api.JmolAdapter;
039import org.jmol.api.JmolStatusListener;
040import org.jmol.api.JmolViewer;
041import org.jmol.util.LoggerInterface;
042import org.slf4j.Logger;
043import org.slf4j.LoggerFactory;
044
045import javax.swing.*;
046
047import java.awt.*;
048import java.awt.event.ActionEvent;
049import java.awt.event.ActionListener;
050import java.text.DecimalFormat;
051import java.util.List;
052
053
054public class JmolPanel
055extends JPrintPanel
056implements ActionListener
057{
058        private static final Logger logger = LoggerFactory.getLogger(JmolPanel.class);
059        
060        private static final long serialVersionUID = -3661941083797644242L;
061
062        private JmolViewer viewer;
063        private JmolAdapter adapter;
064        JmolStatusListener statusListener;
065        final Dimension currentSize = new Dimension();
066        final Rectangle rectClip = new Rectangle();
067
068        Structure structure;
069
070        public JmolPanel() {
071                super();
072                statusListener = new MyJmolStatusListener();
073                adapter = new SmarterJmolAdapter();
074                JmolLoggerAdapter jmolLogger = new JmolLoggerAdapter(LoggerFactory.getLogger(org.jmol.api.JmolViewer.class));
075                org.jmol.util.Logger.setLogger(jmolLogger);
076                org.jmol.util.Logger.setLogLevel( jmolLogger.getLogLevel() );
077                viewer = JmolViewer.allocateViewer(this,
078                                adapter,
079                                null,null,null,null,
080                                statusListener);
081
082        }
083
084        @Override
085        public void paint(Graphics g) {
086
087                getSize(currentSize);
088                g.getClipBounds(rectClip);
089                viewer.renderScreenImage(g, currentSize, rectClip);
090        }
091
092        public void evalString(String rasmolScript){
093
094                viewer.evalString(rasmolScript);
095
096        }
097
098        public void openStringInline(String pdbFile){
099                viewer.openStringInline(pdbFile);
100
101        }
102        public JmolViewer getViewer() {
103                return viewer;
104        }
105
106        public JmolAdapter getAdapter(){
107                return adapter;
108        }
109
110        public JmolStatusListener getStatusListener(){
111                return statusListener;
112        }
113        public void executeCmd(String rasmolScript) {
114                viewer.evalString(rasmolScript);
115        }
116
117        public void setStructure(Structure s)
118        {
119                this.structure = s;
120                String serialized = s.toMMCIF();
121                viewer.openStringInline(serialized);
122                evalString("save STATE state_1");
123        }
124
125        /** assign a custom color to the Jmol chains command.
126         *
127         */
128        public void jmolColorByChain(){
129                String script =
130                                "function color_by_chain(objtype, color_list) {"+ String.format("%n") +
131                                ""+ String.format("%n") +
132                                "                if (color_list) {"+ String.format("%n") +
133                                "                  if (color_list.type == \"string\") {"+ String.format("%n") +
134                                "                    color_list = color_list.split(\",\").trim();"+ String.format("%n") +
135                                "                  }"+ String.format("%n") +
136                                "                } else {"+ String.format("%n") +
137                                "                  color_list = [\"104BA9\",\"AA00A2\",\"C9F600\",\"FFA200\",\"284A7E\",\"7F207B\",\"9FB82E\",\"BF8B30\",\"052D6E\",\"6E0069\",\"83A000\",\"A66A00\",\"447BD4\",\"D435CD\",\"D8FA3F\",\"FFBA40\",\"6A93D4\",\"D460CF\",\"E1FA71\",\"FFCC73\"];"+ String.format("%n") +
138                                "                }"+ String.format("%n") +
139
140                                "                var cmd2 = \"\";"+ String.format("%n") +
141
142                                "                if (!objtype) {"+ String.format("%n") +
143                                "                  var type_list  = [ \"backbone\",\"cartoon\",\"dots\",\"halo\",\"label\",\"meshribbon\",\"polyhedra\",\"rocket\",\"star\",\"strand\",\"strut\",\"trace\"];"+ String.format("%n") +
144                                "                  cmd2 = \"color \" + type_list.join(\" none; color \") + \" none;\";"+ String.format("%n") +
145                                "                  objtype = \"atoms\";"+ String.format("%n") +
146
147                                "                }"+ String.format("%n") +
148
149                                "                var chain_list  = script(\"show chain\").trim().lines;"+ String.format("%n") +
150                                "                var chain_count = chain_list.length;"+ String.format("%n") +
151
152                                "                var color_count = color_list.length;"+ String.format("%n") +
153                                "                var sel = {selected};"+ String.format("%n") +
154                                "                var cmds = \"\";"+ String.format("%n") +
155
156
157                                "                for (var chain_number=1; chain_number<=chain_count; chain_number++) {"+ String.format("%n") +
158                                "                  // remember, Jmol arrays start with 1, but % can return 0"+ String.format("%n") +
159                                "                  cmds += \"select sel and :\" + chain_list[chain_number] + \";color \" + objtype + \" [x\" + color_list[(chain_number-1) % color_count + 1] + \"];\" + cmd2;"+ String.format("%n") +
160                                "                }"+ String.format("%n") +
161                                "                script INLINE @{cmds + \"select sel\"}"+ String.format("%n") +
162                                "}";
163
164                executeCmd(script);
165        }
166
167        /** The user selected one of the Combo boxes...
168         *
169         * @param event an ActionEvent
170         */
171        @Override
172        public void actionPerformed(ActionEvent event) {
173
174                Object mysource = event.getSource();
175
176                if ( ! (mysource instanceof JComboBox )) {
177                        super.actionPerformed(event);
178                        return;
179                }
180
181                @SuppressWarnings("unchecked")
182                JComboBox<String> source = (JComboBox<String>) event.getSource();
183                String value = source.getSelectedItem().toString();
184                evalString("save selection; ");
185
186                String selectLigand = "select ligand;wireframe 0.16;spacefill 0.5; color cpk ;";
187
188                if ( value.equals("Cartoon")){
189                        String script = "hide null; select all;  spacefill off; wireframe off; backbone off;" +
190                                        " cartoon on; " +
191                                        " select ligand; wireframe 0.16;spacefill 0.5; color cpk; " +
192                                        " select *.FE; spacefill 0.7; color cpk ; " +
193                                        " select *.CU; spacefill 0.7; color cpk ; " +
194                                        " select *.ZN; spacefill 0.7; color cpk ; " +
195                                        " select all; ";
196                        this.executeCmd(script);
197                } else if (value.equals("Backbone")){
198                        String script = "hide null; select all; spacefill off; wireframe off; backbone 0.4;" +
199                                        " cartoon off; " +
200                                        " select ligand; wireframe 0.16;spacefill 0.5; color cpk; " +
201                                        " select *.FE; spacefill 0.7; color cpk ; " +
202                                        " select *.CU; spacefill 0.7; color cpk ; " +
203                                        " select *.ZN; spacefill 0.7; color cpk ; " +
204                                        " select all; ";
205                        this.executeCmd(script);
206                } else if (value.equals("CPK")){
207                        String script = "hide null; select all; spacefill off; wireframe off; backbone off;" +
208                                        " cartoon off; cpk on;" +
209                                        " select ligand; wireframe 0.16;spacefill 0.5; color cpk; " +
210                                        " select *.FE; spacefill 0.7; color cpk ; " +
211                                        " select *.CU; spacefill 0.7; color cpk ; " +
212                                        " select *.ZN; spacefill 0.7; color cpk ; " +
213                                        " select all; ";
214                        this.executeCmd(script);
215
216                } else if (value.equals("Ligands")){
217                        this.executeCmd("restrict ligand; cartoon off; wireframe on;  display selected;");
218                } else if (value.equals("Ligands and Pocket")){
219                        this.executeCmd(" select within (6.0,ligand); cartoon off; wireframe on; backbone off; display selected; ");
220                } else if ( value.equals("Ball and Stick")){
221                        String script = "hide null; restrict not water;  wireframe 0.2; spacefill 25%;" +
222                                        " cartoon off; backbone off; " +
223                                        " select ligand; wireframe 0.16; spacefill 0.5; color cpk; " +
224                                        " select *.FE; spacefill 0.7; color cpk ; " +
225                                        " select *.CU; spacefill 0.7; color cpk ; " +
226                                        " select *.ZN; spacefill 0.7; color cpk ; " +
227                                        " select all; ";
228                        this.executeCmd(script);
229                } else if ( value.equals("By Chain")){
230                        jmolColorByChain();
231                        String script = "hide null; select all;set defaultColors Jmol; color_by_chain(\"cartoon\"); color_by_chain(\"\"); " + selectLigand + "; select all; ";
232                        this.executeCmd(script);
233                } else if ( value.equals("Rainbow")) {
234                        this.executeCmd("hide null; select all; set defaultColors Jmol; color group; color cartoon group; " + selectLigand + "; select all; " );
235                } else if ( value.equals("Secondary Structure")){
236                        this.executeCmd("hide null; select all; set defaultColors Jmol; color structure; color cartoon structure;" + selectLigand + "; select all; " );
237
238                } else if ( value.equals("By Element")){
239                        this.executeCmd("hide null; select all; set defaultColors Jmol; color cpk; color cartoon cpk; " + selectLigand + "; select all; ");
240                } else if ( value.equals("By Amino Acid")){
241                        this.executeCmd("hide null; select all; set defaultColors Jmol; color amino; color cartoon amino; " + selectLigand + "; select all; " );
242                } else if ( value.equals("Hydrophobicity") ){
243                        this.executeCmd("hide null; set defaultColors Jmol; select hydrophobic; color red; color cartoon red; select not hydrophobic ; color blue ; color cartoon blue; "+ selectLigand+"; select all; ");
244                } else if ( value.equals("Suggest Domains")){
245                        colorByPDP();
246                } else if ( value.equals("Show SCOP Domains")){
247                        colorBySCOP();
248                }
249                evalString("restore selection; ");
250        }
251
252        private void colorBySCOP() {
253
254                if ( structure == null)
255                        return;
256
257                String pdbId = structure.getPDBCode();
258                if ( pdbId == null)
259                        return;
260                ScopDatabase scop = new ScopInstallation();
261
262                List<ScopDomain> domains = scop.getDomainsForPDB(pdbId);
263                if ( domains == null) {
264                        System.err.println("No SCOP domains found for " + pdbId);
265                        return;
266                }
267                int i = -1;
268                for ( ScopDomain domain : domains){
269                        i++;
270                        if ( i >= ColorUtils.colorWheel.length)
271                                i = 0;
272                        Color c1 = ColorUtils.colorWheel[i];
273                        List<String>ranges = domain.getRanges();
274
275                        for (String range : ranges){
276                                logger.debug(range);
277                                String[] spl = range.split(":");
278                                String script = " select  ";
279                                if ( spl.length > 1 )
280                                        script += spl[1]+":"+spl[0] +"/1;";
281                                else
282                                        script += "*" + spl[0]+"/1;";
283                                script += " color [" + c1.getRed() + ","+c1.getGreen() + "," +c1.getBlue()+"];";
284                                script += " color cartoon [" + c1.getRed() + ","+c1.getGreen() + "," +c1.getBlue()+"] ;";
285                                logger.debug(script);
286                                evalString(script);
287
288                        }
289                }
290
291
292        }
293
294        private void colorByPDP() {
295                logger.debug("colorByPDP");
296                if ( structure == null)
297                        return;
298
299                try {
300                        Atom[] ca = StructureTools.getRepresentativeAtomArray(structure);
301                        List<Domain> domains = LocalProteinDomainParser.suggestDomains(ca);
302                        int i = -1;
303                        for ( Domain dom : domains){
304                                i++;
305                                if ( i > ColorUtils.colorWheel.length)
306                                        i = 0;
307                                //System.out.println("DOMAIN:" + i + " size:" + dom.size + " " +  dom.score);
308                                List<Segment> segments = dom.getSegments();
309                                Color c1 = ColorUtils.colorWheel[i];
310                                //float fraction = 0f;
311                                for ( Segment s : segments){
312                                        //System.out.println("   Segment: " + s);
313                                        //Color c1 = ColorUtils.rotateHue(c, fraction);
314                                        //      fraction += 1.0/(float)segments.size();
315                                        int start = s.getFrom();
316                                        int end = s.getTo();
317                                        Group startG = ca[start].getGroup();
318                                        Group endG = ca[end].getGroup();
319                                        logger.debug("   Segment: " +startG.getResidueNumber() +":" + startG.getChainId() + " - " + endG.getResidueNumber()+":"+endG.getChainId() + " " + s);
320                                        String j1 = startG.getResidueNumber()+"";
321                                        String j2 = endG.getResidueNumber()+":"+endG.getChainId();
322                                        String script = " select  " +j1 +"-" +j2 +"/1;";
323                                        script += " color [" + c1.getRed() + ","+c1.getGreen() + "," +c1.getBlue()+"];";
324                                        script += " color cartoon [" + c1.getRed() + ","+c1.getGreen() + "," +c1.getBlue()+"] ;";
325                                        logger.debug(script);
326                                        evalString(script);
327                                }
328
329                        }
330                } catch (Exception e){
331                        e.printStackTrace();
332                }
333        }
334
335        public void rotateJmol(Matrix jmolRotation) {
336
337                if ( jmolRotation != null) {
338                        double[] zyz = Calc.getZYZEuler(jmolRotation);
339                        DecimalFormat df = new DecimalFormat("0.##");
340
341                        String script = "reset; rotate z "
342                                        + df.format(zyz[0])
343                                        + "; rotate y "
344                                        + df.format(zyz[1])
345                                        +"; rotate z "
346                                        + df.format(zyz[2])+";";
347
348                        executeCmd(script);
349
350                }
351        }
352
353        /** Clean up this instance for garbage collection, to avoid memory leaks...
354         *
355         */
356        public void destroy(){
357
358                executeCmd("zap;");
359                structure = null;
360
361                viewer = null;
362                adapter = null;
363        }
364
365        public static class JmolLoggerAdapter implements LoggerInterface {
366                private Logger slf;
367                public JmolLoggerAdapter(Logger slf) {
368                        this.slf=slf;
369                }
370                public int getLogLevel() {
371                        if( slf.isTraceEnabled() )
372                                return org.jmol.util.Logger.LEVEL_MAX;
373                        if( slf.isDebugEnabled() )
374                                return org.jmol.util.Logger.LEVEL_DEBUG;
375                        if( slf.isInfoEnabled() )
376                                return org.jmol.util.Logger.LEVEL_INFO;
377                        if( slf.isWarnEnabled() )
378                                return org.jmol.util.Logger.LEVEL_WARN;
379                        if( slf.isErrorEnabled() )
380                                return org.jmol.util.Logger.LEVEL_ERROR;
381                        throw new IllegalStateException("Unknown SLF4J error level");
382                }
383                @Override
384                public void debug(String txt) {
385                        slf.debug(txt);
386                }
387                @Override
388                public void info(String txt) {
389                        slf.info(txt);
390                }
391                @Override
392                public void warn(String txt) {
393                        slf.warn(txt);
394                }
395                @Override
396                public void warnEx(String txt, Throwable e) {
397                        slf.warn(txt,e);
398                }
399                @Override
400                public void error(String txt) {
401                        slf.error(txt);
402                }
403                @Override
404                public void errorEx(String txt, Throwable e) {
405                        slf.error(txt,e);
406                }
407                @Override
408                public void fatal(String txt) {
409                        slf.error(txt);
410                }
411                @Override
412                public void fatalEx(String txt, Throwable e) {
413                        slf.error(txt,e);
414                }
415        }
416}