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 at May 26, 2008 021 */ 022package org.biojava.nbio.structure.gui; 023 024import org.biojava.nbio.structure.*; 025import org.biojava.nbio.structure.align.StructurePairAligner; 026import org.biojava.nbio.structure.align.pairwise.AlternativeAlignment; 027import org.biojava.nbio.structure.gui.events.AlignmentPositionListener; 028import org.biojava.nbio.structure.gui.util.AlignedPosition; 029import org.biojava.nbio.structure.gui.util.SequenceMouseListener; 030import org.biojava.nbio.structure.gui.util.SequenceScalePanel; 031import org.biojava.nbio.structure.io.PDBFileReader; 032 033import javax.swing.*; 034import javax.swing.event.ChangeEvent; 035import javax.swing.event.ChangeListener; 036import java.awt.*; 037import java.awt.event.WindowAdapter; 038import java.awt.event.WindowEvent; 039import java.util.ArrayList; 040import java.util.List; 041//import org.slf4j.Logger; 042//import org.slf4j.LoggerFactory; 043 044/** A sequence display that can show the results of a protein structure alignment. 045 * 046 * @author Andreas Prlic 047 * @since 1.7 048 */ 049public class SequenceDisplay extends JPanel implements ChangeListener { 050 051 /** 052 * 053 */ 054 private static final long serialVersionUID = -1829252532712454236L; 055 056 Structure structure1; 057 Structure structure2; 058 AlternativeAlignment alig; 059 StructurePairAligner structurePairAligner; 060 SequenceScalePanel panel1; 061 SequenceScalePanel panel2; 062 063 JSlider residueSizeSlider; 064 JLabel percentageDisplay; 065 066 int[] idx1; 067 int[] idx2; 068 069 /** the maximum value that the scale can get 070 * 071 */ 072 public static final int MAX_SCALE = 10; 073 074 //private static final Logger logger = LoggerFactory.getLogger(SequenceDisplay.class); 075 076 List<AlignedPosition> apos; 077 078 float scale; 079 SequenceMouseListener mouseListener1; 080 SequenceMouseListener mouseListener2; 081 082 083 JLabel label1; 084 JLabel label2; 085 086 public static void main(String[] args){ 087 088 try { 089 PDBFileReader pdbr = new PDBFileReader(); 090 091 //String pdb1 = "1crl"; 092 //String pdb2 = "1ede"; 093 094 String pdb1 = "1buz"; 095 String pdb2 = "5pti"; 096 097 098 // NO NEED TO DO CHANGE ANYTHING BELOW HERE... 099 100 StructurePairAligner sc = new StructurePairAligner(); 101 102 103 // step1 : read molecules 104 105 System.out.println("aligning " + pdb1 + " vs. " + pdb2); 106 107 Structure s1 = pdbr.getStructureById(pdb1); 108 Structure s2 = pdbr.getStructureById(pdb2); 109 110 // step 2 : do the calculations 111 sc.align(s1,s2); 112 113 114 AlternativeAlignment[] aligs = sc.getAlignments(); 115 SequenceDisplay displ = new SequenceDisplay(sc); 116 displ.setStructure1(s1); 117 displ.setStructure2(s2); 118 displ.setAlternativeAlignment(aligs[0]); 119 120 displ.updateDisplay(); 121 122 JFrame frame = new JFrame("Sequences for AlternativeAlignment ["+0+"]"); 123 124 frame.getContentPane().add(displ); 125 126 frame.pack(); 127 frame.setVisible(true); 128 frame.addWindowListener(new WindowAdapter(){ 129 @Override 130 public void windowClosing(WindowEvent e){ 131 JFrame f = (JFrame) e.getSource(); 132 f.setVisible(false); 133 f.dispose(); 134 } 135 136 137 138 }); 139 140 } catch (Exception e){ 141 e.printStackTrace(); 142 } 143 144 } 145 146 public SequenceDisplay(StructurePairAligner structurePairAligner){ 147 super(); 148 149 150 151 structure1 = null; 152 structure2 = null; 153 alig = null; 154 this.structurePairAligner = structurePairAligner; 155 panel1 = new SequenceScalePanel(1); 156 panel2 = new SequenceScalePanel(2); 157 158 mouseListener1 = new SequenceMouseListener(this); 159 160 161 panel1.addMouseListener(mouseListener1); 162 panel1.addMouseMotionListener(mouseListener1); 163 164 165 mouseListener2 = new SequenceMouseListener(this); 166 panel2.addMouseListener(mouseListener2); 167 panel2.addMouseMotionListener(mouseListener2); 168 169 //SequenceMouseListener ml = new SequenceMouseListener(this); 170 //this.addMouseListener(ml); 171 //this.addMouseMotionListener(ml); 172 173 Box vBox = Box.createVerticalBox(); 174 175 176 Box hBox1 = Box.createHorizontalBox(); 177 Box hBox2 = Box.createHorizontalBox(); 178 179 label1 = new JLabel(); 180 hBox1.add(label1); 181 182 label2 = new JLabel(); 183 hBox2.add(label2); 184 185 186 hBox1.add(panel1); 187 hBox2.add(panel2); 188 189 vBox.add(hBox1); 190 vBox.add(hBox2); 191 192 193 int RES_MIN = 1; 194 int RES_MAX = 100; 195 int RES_INIT = 100; 196 residueSizeSlider = new JSlider(JSlider.HORIZONTAL, 197 RES_MIN, RES_MAX, RES_INIT); 198 residueSizeSlider.setInverted(true); 199 //residueSizeSlider.setMajorTickSpacing(5); 200 //residueSizeSlider.setMinorTickSpacing(2); 201 residueSizeSlider.setPaintTicks(false); 202 residueSizeSlider.setPaintLabels(false); 203 residueSizeSlider.addChangeListener(this); 204 //residueSizeSlider.setPreferredSize(new Dimension(100,15)); 205 206 percentageDisplay = new JLabel("100 %"); 207 208 Box hBox = Box.createHorizontalBox(); 209 hBox.setBackground(Color.white); 210 hBox.add(Box.createHorizontalGlue()); 211 hBox.add(residueSizeSlider); 212 hBox.add(percentageDisplay); 213 hBox.add(Box.createHorizontalGlue()); 214 215 //vBox.add(hBox); 216 217 JScrollPane scroll = new JScrollPane(vBox); 218 219 //scroll.setPreferredSize(new Dimension(500,160)); 220 221 Box vBox2 = Box.createVerticalBox(); 222 vBox2.add(scroll); 223 vBox2.add(hBox); 224 225 //vBox2.setPreferredSize(new Dimension(500,160)); 226 //vBox2.setSize(new Dimension(500,160)); 227 //vBox2.setMinimumSize(new Dimension(500,160)); 228 //vBox2.setMaximumSize(new Dimension(500,160)); 229 this.setPreferredSize(new Dimension(500,100)); 230 231 this.add(vBox2); 232 233 this.setLayout(new BoxLayout(this,BoxLayout.Y_AXIS)); 234 235 apos = new ArrayList<AlignedPosition>(); 236 } 237 238 public void clearListeners(){ 239 240 mouseListener1.clearListeners(); 241 mouseListener2.clearListeners(); 242 243 } 244 public void addAlignmentPositionListener(AlignmentPositionListener li){ 245 mouseListener1.addAlignmentPositionListener(li); 246 mouseListener2.addAlignmentPositionListener(li); 247 } 248 249 public StructurePairAligner getStructurePairAligner() { 250 return structurePairAligner; 251 } 252 253 public void setStructurePairAligner(StructurePairAligner structurePairAligner) { 254 this.structurePairAligner = structurePairAligner; 255 } 256 /** get the identical position in the alignment 257 * 258 * @return identical positions for structure1 259 */ 260 public int[] getIdx1() { 261 return idx1; 262 } 263 264 /** set the identical positions in the alignment 265 * 266 * @param idx identical positions for structure1 267 */ 268 private void setIdx1(int[] idx) { 269 this.idx1 = idx; 270 271 } 272 /** get the identical position in the alignment 273 * 274 * @return identical positions for structure2 275 */ 276 public int[] getIdx2() { 277 return idx2; 278 } 279 280 /** set the identical positions in the alignment 281 * 282 * @param idx identical positions for structure2 283 */ 284 private void setIdx2(int[] idx) { 285 this.idx2 = idx; 286 287 } 288 289 290 291 private void buildAligMap(){ 292 apos.clear(); 293 294 int gap = 0; 295 int gpos1 = 0; 296 int gpos2 = 0; 297 298 for (int pos = 0 ; pos < idx1.length ; pos ++){ 299 300 int p1 = idx1[pos]; 301 int p2 = idx2[pos]; 302 303 int end = Math.max(p1,p2); 304 305 //System.out.println("p1: " + p1 + " p2: " + p2 ); 306 // fill up gaps... 307 for (;gap<end;gap++){ 308 309 AlignedPosition m = new AlignedPosition(); 310 if ( gpos1 < p1){ 311 m.setPos1(gpos1); 312 313 gpos1++; 314 } 315 if ( gpos2 < p2){ 316 m.setPos2(gpos2); 317 318 gpos2++; 319 } 320 m.setEquivalent(AlignedPosition.NOT_ALIGNED); 321 322 //System.out.println(m + " => " + end); 323 apos.add(m); 324 } 325 326 // add this aligned position 327 AlignedPosition m = new AlignedPosition(); 328 329 m.setPos1(p1); 330 m.setPos2(p2); 331 m.setEquivalent(AlignedPosition.EQUIVALENT); 332 333 //System.out.println(m); 334 apos.add(m); 335 gpos1++; 336 gpos2++; 337 gap++; 338 339 } 340 341 //System.out.println(apos); 342 343 } 344 345 346 private void setAtoms(Structure s, SequenceScalePanel panel){ 347 if ( structurePairAligner == null){ 348 System.err.println("StructurePairAligner has not been set"); 349 return; 350 } 351 Atom[] ca1 = structurePairAligner.getAlignmentAtoms(s); 352 Chain c = new ChainImpl(); 353 c.setChainID("1"); 354 for (Atom atom : ca1) { 355 356 Group g = atom.getGroup(); 357 358 Chain parentChain = g.getChain(); 359 360 c.addGroup(g); 361 // hack for Jmol? 362 g.setChain(parentChain); 363 } 364 panel.setChain(c); 365 366 } 367 368 //TODO: add a method to allow the display if the structure alignment 369 // has been called with setting the atoms directly 370 371 372 373 public void setStructure1(Structure structure){ 374 this.structure1 = structure; 375 if ( structure != null) { 376 setAtoms(structure1,panel1); 377 label1.setText(structure.getPDBCode()); 378 label1.repaint(); 379 } 380 381 382 } 383 public void setStructure2(Structure structure){ 384 this.structure2 = structure; 385 if ( structure != null){ 386 setAtoms(structure2,panel2); 387 label2.setText(structure.getPDBCode()); 388 label2.repaint(); 389 } 390 } 391 392 public void setAlternativeAlignment(AlternativeAlignment alig){ 393 this.alig = alig; 394 this.setIdx1(alig.getIdx1()); 395 this.setIdx2(alig.getIdx2()); 396 397 buildAligMap(); 398 399 panel1.setAligMap(apos); 400 panel2.setAligMap(apos); 401 402 updateDisplay(); 403 404 405 } 406 public List<AlignedPosition> getAligMap(){ 407 return apos; 408 } 409 410 @Override 411 public void stateChanged(ChangeEvent e) { 412 413 JSlider source = (JSlider)e.getSource(); 414 //if (!source.getValueIsAdjusting()) { 415 416 int residueSize = source.getValue(); 417 calcScale(residueSize); 418 419 updatePercentageDisplay(); 420 421 422 423 this.repaint(); 424 this.revalidate(); 425 426 //this.updateUI(); 427 //int width = getTotalWidth(); 428 //int height = getTotalHeight(); 429 //Dimension d = new Dimension(width,height); 430 //logger.info("setting preferred size" + width + " " + height); 431 //this.setPreferredSize(d); 432 //this.setSize(d); 433 // } 434 435 436 } 437 public void updateDisplay(){ 438 439 int residueSize = residueSizeSlider.getValue(); 440 calcScale(residueSize); 441 updatePercentageDisplay(); 442 this.repaint(); 443 this.revalidate(); 444 445 } 446 private void updatePercentageDisplay(){ 447 int perc = residueSizeSlider.getValue(); 448 percentageDisplay.setText(perc+ " %"); 449 } 450 451 private int getMaxSequenceLength(){ 452 int l1 = panel1.getChain().getAtomGroups(GroupType.AMINOACID).size(); 453 int l2 = panel2.getChain().getAtomGroups(GroupType.AMINOACID).size(); 454 if ( l1 > l2) 455 return l1; 456 else return l2; 457 } 458 459 460 461 /** calculate the float that is used for display. 462 * 1 * scale = size of 1 amino acid (in pixel). 463 * maximum @see MAX_SCALE 464 * @param zoomFactor 465 * @return a float that is the display "scale" - an internal value required for paintin. 466 * user should only interact with the zoomfactor ... 467 */ 468 private float getScaleForZoom(int zoomFactor){ 469 470 if ( zoomFactor > 100) 471 zoomFactor = 100; 472 if ( zoomFactor < 1) 473 zoomFactor = 1; 474 475 476 int DEFAULT_X_START 477 = SequenceScalePanel.DEFAULT_X_START; 478 int DEFAULT_X_RIGHT_BORDER = SequenceScalePanel.DEFAULT_X_RIGHT_BORDER; 479 480 int seqLength = getMaxSequenceLength(); 481 // the maximum width depends on the size of the parent Component 482 483 int width=getWidth(); 484 485 486 float s = width / (float) ( seqLength + DEFAULT_X_START + DEFAULT_X_RIGHT_BORDER ); 487 //logger.info("scale for 100% " + s + " " + seqLength + " " + zoomFactor); 488 489 s = (100) * s / ( zoomFactor * 1.0f) ; 490 491 if ( s > MAX_SCALE) 492 s = MAX_SCALE; 493 494 //logger.info("but changed to " + s); 495 return s; 496 } 497 498 /** a value of 100 means that the whole sequence should be displayed in the current visible window 499 * a factor of 1 means that one amino acid shoud be drawn as big as possible 500 * 501 * @param zoomFactor - a value between 1 and 100 502 * 503 * 504 */ 505 public void calcScale(int zoomFactor){ 506 507 float s = getScaleForZoom(zoomFactor); 508 scale = s; 509 //logger.info("calc scale zoom:"+zoomFactor+ " s: " + s); 510 panel1.setScale(s); 511 panel2.setScale(s); 512 panel1.repaint(); 513 panel2.repaint(); 514 515 516 //return scale; 517 518 } 519 520 public float getScale(){ 521 return scale; 522 } 523 524} 525