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}