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, boolean useMmtf) { 131 132 this.structure = s; 133 134 if (useMmtf) { 135 try ( 136 PipedOutputStream out = new PipedOutputStream(); 137 // Viewer requires a BufferedInputStream for reflection 138 InputStream in = new BufferedInputStream(new PipedInputStream(out)); 139 ) { 140 new Thread((Runnable)() -> { 141 try { 142 MmtfActions.writeToOutputStream(s,out); 143 } catch (Exception e) { 144 logger.error("Error generating MMTF output for {}", 145 s.getStructureIdentifier()==null ? s.getStructureIdentifier().getIdentifier() : s.getName(), e); 146 } 147 }).start(); 148 viewer.openReader(null, in); 149 } catch (IOException e) { 150 logger.error("Error transfering {} to Jmol", 151 s.getStructureIdentifier()==null ? s.getStructureIdentifier().getIdentifier() : s.getName(), e); 152 } 153 } else { 154 // Use mmCIF format 155 String serialized = s.toMMCIF(); 156 viewer.openStringInline(serialized); 157 158 } 159 160 evalString("save STATE state_1"); 161 162 } 163 164 public void setStructure(final Structure s) { 165 // Set the default to MMCIF (until issue #629 is fixed) 166 setStructure(s, false); 167 } 168 169 /** assign a custom color to the Jmol chains command. 170 * 171 */ 172 public void jmolColorByChain(){ 173 String script = 174 "function color_by_chain(objtype, color_list) {"+ String.format("%n") + 175 ""+ String.format("%n") + 176 " if (color_list) {"+ String.format("%n") + 177 " if (color_list.type == \"string\") {"+ String.format("%n") + 178 " color_list = color_list.split(\",\").trim();"+ String.format("%n") + 179 " }"+ String.format("%n") + 180 " } else {"+ String.format("%n") + 181 " 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") + 182 " }"+ String.format("%n") + 183 184 " var cmd2 = \"\";"+ String.format("%n") + 185 186 " if (!objtype) {"+ String.format("%n") + 187 " var type_list = [ \"backbone\",\"cartoon\",\"dots\",\"halo\",\"label\",\"meshribbon\",\"polyhedra\",\"rocket\",\"star\",\"strand\",\"strut\",\"trace\"];"+ String.format("%n") + 188 " cmd2 = \"color \" + type_list.join(\" none; color \") + \" none;\";"+ String.format("%n") + 189 " objtype = \"atoms\";"+ String.format("%n") + 190 191 " }"+ String.format("%n") + 192 193 " var chain_list = script(\"show chain\").trim().lines;"+ String.format("%n") + 194 " var chain_count = chain_list.length;"+ String.format("%n") + 195 196 " var color_count = color_list.length;"+ String.format("%n") + 197 " var sel = {selected};"+ String.format("%n") + 198 " var cmds = \"\";"+ String.format("%n") + 199 200 201 " for (var chain_number=1; chain_number<=chain_count; chain_number++) {"+ String.format("%n") + 202 " // remember, Jmol arrays start with 1, but % can return 0"+ String.format("%n") + 203 " cmds += \"select sel and :\" + chain_list[chain_number] + \";color \" + objtype + \" [x\" + color_list[(chain_number-1) % color_count + 1] + \"];\" + cmd2;"+ String.format("%n") + 204 " }"+ String.format("%n") + 205 " script INLINE @{cmds + \"select sel\"}"+ String.format("%n") + 206 "}"; 207 208 executeCmd(script); 209 } 210 211 /** The user selected one of the Combo boxes... 212 * 213 * @param event an ActionEvent 214 */ 215 @Override 216 public void actionPerformed(ActionEvent event) { 217 218 Object mysource = event.getSource(); 219 220 if ( ! (mysource instanceof JComboBox )) { 221 super.actionPerformed(event); 222 return; 223 } 224 225 @SuppressWarnings("unchecked") 226 JComboBox<String> source = (JComboBox<String>) event.getSource(); 227 String value = source.getSelectedItem().toString(); 228 evalString("save selection; "); 229 230 String selectLigand = "select ligand;wireframe 0.16;spacefill 0.5; color cpk ;"; 231 232 if ( value.equals("Cartoon")){ 233 String script = "hide null; select all; spacefill off; wireframe off; backbone off;" + 234 " cartoon on; " + 235 " select ligand; wireframe 0.16;spacefill 0.5; color cpk; " + 236 " select *.FE; spacefill 0.7; color cpk ; " + 237 " select *.CU; spacefill 0.7; color cpk ; " + 238 " select *.ZN; spacefill 0.7; color cpk ; " + 239 " select all; "; 240 this.executeCmd(script); 241 } else if (value.equals("Backbone")){ 242 String script = "hide null; select all; spacefill off; wireframe off; backbone 0.4;" + 243 " cartoon off; " + 244 " select ligand; wireframe 0.16;spacefill 0.5; color cpk; " + 245 " select *.FE; spacefill 0.7; color cpk ; " + 246 " select *.CU; spacefill 0.7; color cpk ; " + 247 " select *.ZN; spacefill 0.7; color cpk ; " + 248 " select all; "; 249 this.executeCmd(script); 250 } else if (value.equals("CPK")){ 251 String script = "hide null; select all; spacefill off; wireframe off; backbone off;" + 252 " cartoon off; cpk on;" + 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 260 } else if (value.equals("Ligands")){ 261 this.executeCmd("restrict ligand; cartoon off; wireframe on; display selected;"); 262 } else if (value.equals("Ligands and Pocket")){ 263 this.executeCmd(" select within (6.0,ligand); cartoon off; wireframe on; backbone off; display selected; "); 264 } else if ( value.equals("Ball and Stick")){ 265 String script = "hide null; restrict not water; wireframe 0.2; spacefill 25%;" + 266 " cartoon off; backbone off; " + 267 " select ligand; wireframe 0.16; spacefill 0.5; color cpk; " + 268 " select *.FE; spacefill 0.7; color cpk ; " + 269 " select *.CU; spacefill 0.7; color cpk ; " + 270 " select *.ZN; spacefill 0.7; color cpk ; " + 271 " select all; "; 272 this.executeCmd(script); 273 } else if ( value.equals("By Chain")){ 274 jmolColorByChain(); 275 String script = "hide null; select all;set defaultColors Jmol; color_by_chain(\"cartoon\"); color_by_chain(\"\"); " + selectLigand + "; select all; "; 276 this.executeCmd(script); 277 } else if ( value.equals("Rainbow")) { 278 this.executeCmd("hide null; select all; set defaultColors Jmol; color group; color cartoon group; " + selectLigand + "; select all; " ); 279 } else if ( value.equals("Secondary Structure")){ 280 this.executeCmd("hide null; select all; set defaultColors Jmol; color structure; color cartoon structure;" + selectLigand + "; select all; " ); 281 282 } else if ( value.equals("By Element")){ 283 this.executeCmd("hide null; select all; set defaultColors Jmol; color cpk; color cartoon cpk; " + selectLigand + "; select all; "); 284 } else if ( value.equals("By Amino Acid")){ 285 this.executeCmd("hide null; select all; set defaultColors Jmol; color amino; color cartoon amino; " + selectLigand + "; select all; " ); 286 } else if ( value.equals("Hydrophobicity") ){ 287 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; "); 288 } else if ( value.equals("Suggest Domains")){ 289 colorByPDP(); 290 } else if ( value.equals("Show SCOP Domains")){ 291 colorBySCOP(); 292 } 293 evalString("restore selection; "); 294 } 295 296 private void colorBySCOP() { 297 298 if ( structure == null) 299 return; 300 301 String pdbId = structure.getPDBCode(); 302 if ( pdbId == null) 303 return; 304 ScopDatabase scop = new ScopInstallation(); 305 306 List<ScopDomain> domains = scop.getDomainsForPDB(pdbId); 307 if ( domains == null) { 308 System.err.println("No SCOP domains found for " + pdbId); 309 return; 310 } 311 int i = -1; 312 for ( ScopDomain domain : domains){ 313 i++; 314 if ( i >= ColorUtils.colorWheel.length) 315 i = 0; 316 Color c1 = ColorUtils.colorWheel[i]; 317 List<String>ranges = domain.getRanges(); 318 319 for (String range : ranges){ 320 logger.debug(range); 321 String[] spl = range.split(":"); 322 String script = " select "; 323 if ( spl.length > 1 ) 324 script += spl[1]+":"+spl[0] +"/1;"; 325 else 326 script += "*" + spl[0]+"/1;"; 327 script += " color [" + c1.getRed() + ","+c1.getGreen() + "," +c1.getBlue()+"];"; 328 script += " color cartoon [" + c1.getRed() + ","+c1.getGreen() + "," +c1.getBlue()+"] ;"; 329 logger.debug(script); 330 evalString(script); 331 332 } 333 } 334 335 336 } 337 338 private void colorByPDP() { 339 logger.debug("colorByPDP"); 340 if ( structure == null) 341 return; 342 343 try { 344 Atom[] ca = StructureTools.getRepresentativeAtomArray(structure); 345 List<Domain> domains = LocalProteinDomainParser.suggestDomains(ca); 346 int i = -1; 347 for ( Domain dom : domains){ 348 i++; 349 if ( i > ColorUtils.colorWheel.length) 350 i = 0; 351 //System.out.println("DOMAIN:" + i + " size:" + dom.size + " " + dom.score); 352 List<Segment> segments = dom.getSegments(); 353 Color c1 = ColorUtils.colorWheel[i]; 354 //float fraction = 0f; 355 for ( Segment s : segments){ 356 //System.out.println(" Segment: " + s); 357 //Color c1 = ColorUtils.rotateHue(c, fraction); 358 // fraction += 1.0/(float)segments.size(); 359 int start = s.getFrom(); 360 int end = s.getTo(); 361 Group startG = ca[start].getGroup(); 362 Group endG = ca[end].getGroup(); 363 logger.debug(" Segment: " +startG.getResidueNumber() +":" + startG.getChainId() + " - " + endG.getResidueNumber()+":"+endG.getChainId() + " " + s); 364 String j1 = startG.getResidueNumber()+""; 365 String j2 = endG.getResidueNumber()+":"+endG.getChainId(); 366 String script = " select " +j1 +"-" +j2 +"/1;"; 367 script += " color [" + c1.getRed() + ","+c1.getGreen() + "," +c1.getBlue()+"];"; 368 script += " color cartoon [" + c1.getRed() + ","+c1.getGreen() + "," +c1.getBlue()+"] ;"; 369 logger.debug(script); 370 evalString(script); 371 } 372 373 } 374 } catch (Exception e){ 375 e.printStackTrace(); 376 } 377 } 378 379 public void rotateJmol(Matrix jmolRotation) { 380 381 if ( jmolRotation != null) { 382 double[] zyz = Calc.getZYZEuler(jmolRotation); 383 DecimalFormat df = new DecimalFormat("0.##"); 384 385 String script = "reset; rotate z " 386 + df.format(zyz[0]) 387 + "; rotate y " 388 + df.format(zyz[1]) 389 +"; rotate z " 390 + df.format(zyz[2])+";"; 391 392 executeCmd(script); 393 394 } 395 } 396 397 /** Clean up this instance for garbage collection, to avoid memory leaks... 398 * 399 */ 400 public void destroy(){ 401 402 executeCmd("zap;"); 403 structure = null; 404 405 viewer = null; 406 adapter = null; 407 } 408 409 public static class JmolLoggerAdapter implements LoggerInterface { 410 private Logger slf; 411 public JmolLoggerAdapter(Logger slf) { 412 this.slf=slf; 413 } 414 public int getLogLevel() { 415 if( slf.isTraceEnabled() ) 416 return org.jmol.util.Logger.LEVEL_MAX; 417 if( slf.isDebugEnabled() ) 418 return org.jmol.util.Logger.LEVEL_DEBUG; 419 if( slf.isInfoEnabled() ) 420 return org.jmol.util.Logger.LEVEL_INFO; 421 if( slf.isWarnEnabled() ) 422 return org.jmol.util.Logger.LEVEL_WARN; 423 if( slf.isErrorEnabled() ) 424 return org.jmol.util.Logger.LEVEL_ERROR; 425 throw new IllegalStateException("Unknown SLF4J error level"); 426 } 427 @Override 428 public void debug(String txt) { 429 slf.debug(txt); 430 } 431 @Override 432 public void info(String txt) { 433 slf.info(txt); 434 } 435 @Override 436 public void warn(String txt) { 437 slf.warn(txt); 438 } 439 @Override 440 public void warnEx(String txt, Throwable e) { 441 slf.warn(txt,e); 442 } 443 @Override 444 public void error(String txt) { 445 slf.error(txt); 446 } 447 @Override 448 public void errorEx(String txt, Throwable e) { 449 slf.error(txt,e); 450 } 451 @Override 452 public void fatal(String txt) { 453 slf.error(txt); 454 } 455 @Override 456 public void fatalEx(String txt, Throwable e) { 457 slf.error(txt,e); 458 } 459 } 460}