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 Nov 8, 2005 021 * 022 */ 023package org.biojava.nbio.structure.gui.util; 024 025import org.biojava.nbio.structure.*; 026 027import javax.swing.*; 028import java.awt.*; 029import java.util.ArrayList; 030import java.util.Iterator; 031import java.util.List; 032 033 034/** A class that draws a Sequence as a rectangle, a scale display over it. 035 * 036 * @author Andreas Prlic 037 * @since 1.7 038 */ 039public class SequenceScalePanel 040extends JPanel{ 041 042 static final long serialVersionUID = 7893248902423l; 043 044 //private static final Logger logger = LoggerFactory.getLogger(SequenceScalePanel.class); 045 046 public static final int DEFAULT_X_START = 10 ; 047 public static final int DEFAULT_X_RIGHT_BORDER = 40 ; 048 public static final int DEFAULT_Y_START = 0 ; 049 public static final int DEFAULT_Y_STEP = 10 ; 050 public static final int DEFAULT_Y_HEIGHT = 8 ;// the size of the box 051 public static final int DEFAULT_Y_BOTTOM = 16 ; 052 public static final int LINE_HEIGHT = 10 ; 053 public static final int MINIMUM_HEIGHT = 20; 054 public static final Color SEQUENCE_COLOR = Color.LIGHT_GRAY; 055 public static final Color SCALE_COLOR = Color.black; 056 public static final Color TEXT_SCALE_COLOR = Color.GRAY; 057 public static final Color IDX_COLOR = Color.yellow; 058 public static final Color GAP_COLOR = Color.white; 059 public static final Color BACKGROUND_COLOR; 060 public static final Font seqFont ; 061 062 // the scale value after which to show the sequence as text 063 private static final int SEQUENCE_SHOW = 9; 064 065 // the height of the panel 066 public static final int SIZE = 20; 067 068 Chain chain; 069 int chainLength; 070 float scale; 071 Character[] seqArr; 072 073 CoordManager coordManager; 074 075 076 077 int position; 078 List<AlignedPosition> apos; 079 080 static { 081 082 String fontName = "Helvetica"; 083 084 int fsize = 12; 085 seqFont = new Font(fontName,Font.PLAIN,fsize); 086 087 String col1 = "#FFFFFF"; 088 BACKGROUND_COLOR = Color.decode(col1); 089 090 } 091 092 093 public SequenceScalePanel(int position) { 094 super(); 095 this.position = position; 096 this.setBackground(BACKGROUND_COLOR); 097 098 chain = new ChainImpl(); 099 setDoubleBuffered(true); 100 101 seqArr = new Character[0]; 102 chainLength = 0; 103 scale = 1.0f; 104 105 setPrefSize(); 106 coordManager = new CoordManager(); 107 108 apos = new ArrayList<AlignedPosition>(); 109 110 } 111 112 113 114 115 private void setPrefSize() { 116 117 int length = chainLength ; 118 int l = Math.round(length*scale) + DEFAULT_X_START + DEFAULT_X_RIGHT_BORDER ; 119 if ( l < 60){ 120 l = 60; 121 } 122 this.setPreferredSize(new Dimension(l,SIZE)); 123 124 } 125 126 public void setAligMap(List<AlignedPosition> apos){ 127 this.apos = apos; 128 129 if ( apos.size() == 0) 130 return; 131 132 AlignedPosition last = apos.get(apos.size()-1); 133 //System.out.println("got last aligned:" +last); 134 if ( last.getPos(position) != -1){ 135 // add the end of the chain... 136 int idxlast = last.getPos(position); 137 138 139 for (;idxlast < chainLength;idxlast++){ 140 AlignedPosition m = new AlignedPosition(); 141 m.setPos(position,idxlast); 142 143 apos.add(m); 144 } 145 } 146 147 } 148 149 public synchronized void setChain(Chain c){ 150 151 List<Group> a = c.getAtomGroups(GroupType.AMINOACID); 152 153 seqArr = new Character[a.size()]; 154 155 chain = new ChainImpl(); 156 157 Iterator<Group> iter = a.iterator(); 158 int i = 0; 159 while (iter.hasNext()){ 160 AminoAcid aa = (AminoAcid) iter.next(); 161 162 // preserver original hierarchy ... for highlighting in Jmol 163 Chain old = aa.getChain(); 164 chain.addGroup(aa); 165 aa.setChain(old); 166 seqArr[i] = aa.getAminoType(); 167 i++; 168 } 169 170 chainLength = i; 171 coordManager.setLength(chainLength); 172 setPrefSize(); 173 174 this.repaint(); 175 } 176 177 public Chain getChain(){ 178 return chain; 179 } 180 181 public synchronized float getScale(){ 182 return scale; 183 } 184 185 186 public void setScale(float scale) { 187 188 this.scale=scale; 189 coordManager.setScale(scale); 190 setPrefSize(); 191 192 this.repaint(); 193 this.revalidate(); 194 } 195 196 /** set some default rendering hints, like text antialiasing on 197 * 198 * @param g2D the graphics object to set the defaults on 199 */ 200 protected void setPaintDefaults(Graphics2D g2D){ 201 g2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, 202 RenderingHints.VALUE_TEXT_ANTIALIAS_ON); 203 g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 204 RenderingHints.VALUE_ANTIALIAS_ON); 205 206 g2D.setFont(seqFont); 207 } 208 209 @Override 210 public void paintComponent(Graphics g){ 211 212 g.setColor(BACKGROUND_COLOR); 213 214 Rectangle drawHere = g.getClipBounds(); 215 g.fillRect(drawHere.x,drawHere.y, drawHere.width, drawHere.height); 216 217 218 Graphics2D g2D =(Graphics2D) g; 219 220 setPaintDefaults(g2D); 221 222 int y = 1; 223 224 225 // draw the scale 226 227 y = drawScale(g2D,1); 228 229 // draw the background for identical residues 230 drawIdx(g2D,y); 231 232 // sequence 233 y = drawSequence(g2D,y); 234 235 236 } 237 238 239 240 /** draw the Scale 241 * 242 * @param g2D 243 * @param y the height on which to draw the scale 244 * @return the new y position 245 */ 246 protected int drawScale(Graphics2D g2D, int y){ 247 248 // only draw within the ranges of the Clip 249 Rectangle drawHere = g2D.getClipBounds(); 250 251 g2D.setColor(SCALE_COLOR); 252 253 int aminosize = Math.round(1*scale); 254 if ( aminosize < 1) 255 aminosize = 1; 256 257 258 259 int startpos = coordManager.getSeqPos(drawHere.x); 260 int endpos = coordManager.getSeqPos(drawHere.x+drawHere.width); 261 262 if ( endpos > apos.size()) 263 endpos = apos.size(); 264 265 int l = endpos - startpos + 1 ; 266 267 int drawStart = coordManager.getPanelPos(startpos); 268 int drawEnd = coordManager.getPanelPos(l) - DEFAULT_X_START + aminosize; 269 270// System.out.println("SeqScalePanel drawing scale s:" + startpos + " e: " + endpos + 271// " ps: " + drawStart + " pe:" + drawEnd + " draw.x " + drawHere.x + " draw.w " + drawHere.width + 272// " scale " + scale); 273 274// the frame around the sequence box 275 if ( scale < SEQUENCE_SHOW){ 276 g2D.setColor(SEQUENCE_COLOR); 277 //g2D.setColor(Color.blue); 278 Rectangle seqline = new Rectangle(drawStart, y, drawEnd, LINE_HEIGHT); 279 280 //g2D= (Graphics2D)g; 281 g2D.fill(seqline); 282 //g2D.setColor(Color.blue); 283 //g2D.draw(seqline); 284 } 285 286 // the top line for the scale 287 g2D.setColor(SCALE_COLOR); 288 Rectangle baseline = new Rectangle(drawStart, y, drawEnd, 2); 289 g2D.fill(baseline); 290 291 292 // draw the vertical ticks 293 294 295 int lineH = 11; 296 if ( scale <= 3) 297 lineH = 8; 298 299 for (int gap =startpos ; ((gap<= endpos) && ( gap < apos.size())); gap++){ 300 int xpos = coordManager.getPanelPos(gap) ; 301 302 AlignedPosition m = apos.get(gap); 303 if ( m.getPos(position) == -1 ){ 304 // a gap position 305 g2D.setColor(GAP_COLOR); 306 g2D.fillRect(xpos, y+2, aminosize+1, y+lineH); 307 g2D.setColor(GAP_COLOR); 308 continue; 309 } 310 311 int i = m.getPos(position); 312 313 if ( ((i+1)%100) == 0 ) { 314 315 if ( scale> 0.1) { 316 g2D.setColor(TEXT_SCALE_COLOR); 317 g2D.fillRect(xpos, y+2, aminosize, y+lineH); 318 g2D.setColor(SCALE_COLOR); 319 if ( scale < SEQUENCE_SHOW) 320 g2D.drawString(String.valueOf(i+1), xpos, y+DEFAULT_Y_STEP); 321 } 322 323 }else if ( ((i+1)%50) == 0 ) { 324 if ( scale>1.4) { 325 g2D.setColor(TEXT_SCALE_COLOR); 326 g2D.fillRect(xpos,y+2, aminosize, y+lineH); 327 g2D.setColor(SCALE_COLOR); 328 if ( scale < SEQUENCE_SHOW) 329 g2D.drawString(String.valueOf(i+1), xpos, y+DEFAULT_Y_STEP); 330 331 } 332 333 } else if ( ((i+1)%10) == 0 ) { 334 if ( scale> 3) { 335 g2D.setColor(TEXT_SCALE_COLOR); 336 g2D.fillRect(xpos, y+2, aminosize, y+lineH); 337 g2D.setColor(SCALE_COLOR); 338 if ( scale < SEQUENCE_SHOW) 339 g2D.drawString(String.valueOf(i+1), xpos, y+DEFAULT_Y_STEP); 340 341 } 342 } 343 } 344 345 346 int length = chainLength; 347 if ( endpos >= length-1) { 348 349 int endPanel = coordManager.getPanelPos(endpos); 350 g2D.drawString(String.valueOf(length), endPanel+10,y+DEFAULT_Y_STEP); 351 } 352 353 return y ; 354 355 } 356 357 protected void drawIdx(Graphics2D g2D, int y){ 358 359 int aminosize = Math.round(1*scale); 360 if ( aminosize < 1) 361 aminosize = 1; 362 363 // only draw within the ranges of the Clip 364 Rectangle drawHere = g2D.getClipBounds(); 365 int startpos = coordManager.getSeqPos(drawHere.x); 366 //int endpos = coordManager.getSeqPos(drawHere.x+drawHere.width-2); 367 368 369 Composite oldComp = g2D.getComposite(); 370 g2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.8f)); 371 //logger.info("paint l " + l + " length " + length ); 372 373 if ( startpos < 0) 374 startpos = 999; 375 376 g2D.setColor(IDX_COLOR); 377 int lineH = 11; 378 if ( scale <= 3) 379 lineH = 8; 380 381 int i = startpos; 382 383 384 // display the actual sequence!; 385 for ( int gap = startpos ; gap < apos.size() ;gap++){ 386 int xpos = coordManager.getPanelPos(gap) ; 387 388 AlignedPosition m = apos.get(gap); 389 if ( m.getEquivalent() == AlignedPosition.NOT_ALIGNED){ 390 // a gap position 391 continue; 392 } 393 394 i = m.getPos(position); 395 396 397 398 399 for (AlignedPosition xi : apos ) { 400 if (xi.getPos(position)!= -1) 401 if ( i == xi.getPos(position)){ 402 g2D.fillRect(xpos, y+2, aminosize, y+lineH); 403 break; 404 } 405 } 406 // TODO: 407 // color amino acids by hydrophobicity 408 409 410 } 411 412 g2D.setComposite(oldComp); 413 414 } 415 416 /** draw the Amino acid sequence 417 * 418 * @param g2D 419 * @param y .. height of line to draw the sequence onto 420 * @return the new y value 421 */ 422 protected int drawSequence(Graphics2D g2D, int y){ 423 //g2D.drawString(panelName,10,10); 424 425 g2D.setColor(SEQUENCE_COLOR); 426 int aminosize = Math.round(1*scale); 427 if ( aminosize < 1) 428 aminosize = 1; 429 430 // only draw within the ranges of the Clip 431 Rectangle drawHere = g2D.getClipBounds(); 432 int startpos = coordManager.getSeqPos(drawHere.x); 433 //int endpos = coordManager.getSeqPos(drawHere.x+drawHere.width-2); 434 435 436 Composite oldComp = g2D.getComposite(); 437 g2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.8f)); 438 //logger.info("paint l " + l + " length " + length ); 439 440 if ( startpos < 0) 441 startpos = 999; 442 443 if ( scale > SEQUENCE_SHOW){ 444 g2D.setColor(Color.black); 445 446 447 //g2D.setColor(SCALE_COLOR); 448 449 int i = startpos; 450 451 // display the actual sequence!; 452 for ( int gap = startpos ; gap < apos.size() ;gap++){ 453 int xpos = coordManager.getPanelPos(gap) ; 454 455 AlignedPosition m = apos.get(gap); 456 if (m.getPos(position) == -1){ 457 // a gap position 458 g2D.drawString("-",xpos+1,y+2+DEFAULT_Y_STEP); 459 continue; 460 } 461 462 i = m.getPos(position); 463 464 // TODO: 465 // color amino acids by hydrophobicity 466 467 g2D.drawString(seqArr[i].toString(),xpos+1,y+2+DEFAULT_Y_STEP); 468 } 469 470// in full sequence mode we need abit more space to look nice 471 472 y+=2; 473 } 474 g2D.setComposite(oldComp); 475 y+= DEFAULT_Y_STEP + 2; 476 return y; 477 } 478 479 480}