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