dea8ddc6e09c649daa1ca4066f7643dd589470d8
[jalview.git] / src / jalview / appletgui / OverviewCanvas.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.appletgui;
22
23 import jalview.datamodel.SequenceI;
24 import jalview.renderer.seqfeatures.FeatureColourFinder;
25 import jalview.viewmodel.OverviewDimensions;
26
27 import java.awt.Color;
28 import java.awt.Dimension;
29 import java.awt.Frame;
30 import java.awt.Graphics;
31 import java.awt.Image;
32
33 import javax.swing.JComponent;
34
35 public class OverviewCanvas extends JComponent
36 {
37   // This is set true if the alignment view changes whilst
38   // the overview is being calculated
39   private volatile boolean restart = false;
40
41   private volatile boolean updaterunning = false;
42
43   private OverviewDimensions od;
44
45   private Image miniMe;
46
47   private Image offscreen;
48
49   private AlignViewport av;
50
51   private AlignmentPanel ap;
52
53   // Can set different properties in this seqCanvas than
54   // main visible SeqCanvas
55   private SequenceRenderer sr;
56
57   private FeatureRenderer fr;
58
59   private Frame nullFrame;
60
61   public OverviewCanvas(OverviewDimensions overviewDims,
62           AlignViewport alignvp, AlignmentPanel alignp)
63   {
64     od = overviewDims;
65     av = alignp.av;
66     ap = alignp;
67
68     nullFrame = new Frame();
69     nullFrame.addNotify();
70
71     sr = new SequenceRenderer(av);
72     sr.graphics = nullFrame.getGraphics();
73     sr.renderGaps = false;
74     sr.forOverview = true;
75     fr = new FeatureRenderer(av);
76   }
77
78   /*
79    * Signals to drawing code that the associated alignment viewport
80    * has changed and a redraw will be required
81    */
82   public boolean restartDraw()
83   {
84     synchronized (this)
85     {
86       if (updaterunning)
87       {
88         restart = true;
89       }
90       else
91       {
92         updaterunning = true;
93       }
94       return restart;
95     }
96   }
97
98   public void draw(boolean showSequenceFeatures, boolean showAnnotation)
99   {
100     miniMe = null;
101
102     if (av.isShowSequenceFeatures())
103     {
104       fr.transferSettings(ap.seqPanel.seqCanvas.getFeatureRenderer());
105     }
106
107     setPreferredSize(new Dimension(od.getWidth(), od.getHeight()));
108
109     miniMe = nullFrame.createImage(od.getWidth(), od.getHeight());
110     offscreen = nullFrame.createImage(od.getWidth(), od.getHeight());
111
112     Graphics mg = miniMe.getGraphics();
113
114     int alwidth = av.getAlignment().getWidth();
115     int alheight = av.getAlignment().getAbsoluteHeight();
116     float sampleCol = alwidth / (float) od.getWidth();
117     float sampleRow = alheight / (float) od.getSequencesHeight();
118
119     buildImage(sampleRow, sampleCol, mg);
120
121     // check for conservation annotation to make sure overview works for DNA too
122     if (showAnnotation)
123     {
124       for (int col = 0; col < od.getWidth() && !restart; col++)
125       {
126         mg.translate(col, od.getSequencesHeight());
127         ap.annotationPanel.renderer.drawGraph(mg,
128                 av.getAlignmentConservationAnnotation(),
129                 av.getAlignmentConservationAnnotation().annotations,
130                 (int) (sampleCol) + 1, od.getGraphHeight(),
131                 (int) (col * sampleCol), (int) (col * sampleCol) + 1);
132         mg.translate(-col, -od.getSequencesHeight());
133       }
134     }
135     System.gc();
136
137     if (restart)
138     {
139       restart = false;
140       draw(showSequenceFeatures, showAnnotation);
141     }
142     else
143     {
144       updaterunning = false;
145     }
146   }
147
148   /*
149    * Build the overview panel image
150    */
151   private void buildImage(float sampleRow, float sampleCol, Graphics mg)
152   {
153     int lastcol = 0;
154     int lastrow = 0;
155     int xstart = 0;
156     int ystart = 0;
157     Color color = Color.yellow;
158     int sameRow = 0;
159     int sameCol = 0;
160
161     SequenceI seq = null;
162     FeatureColourFinder finder = new FeatureColourFinder(fr);
163
164     final boolean hasHiddenCols = av.hasHiddenColumns();
165     boolean hiddenRow = false;
166
167     for (int row = 0; row < od.getSequencesHeight() && !restart; row++)
168     {
169       if ((int) (row * sampleRow) == lastrow)
170       {
171         sameRow++;
172       }
173       else
174       {
175         // get the sequence which would be at alignment index 'lastrow' if no
176         // rows were hidden, and determine whether it is hidden or not
177         hiddenRow = av.getAlignment().isHidden(lastrow);
178         seq = av.getAlignment().getSequenceAtAbsoluteIndex(lastrow);
179
180         for (int col = 0; col < od.getWidth(); col++)
181         {
182           if ((int) (col * sampleCol) == lastcol
183                   && (int) (row * sampleRow) == lastrow)
184           {
185             sameCol++;
186           }
187           else
188           {
189             lastcol = (int) (col * sampleCol);
190
191             color = getColumnColourFromSequence(seq, hiddenRow,
192                     hasHiddenCols, lastcol, finder);
193
194             mg.setColor(color);
195             if (sameCol == 1 && sameRow == 1)
196             {
197               mg.drawLine(xstart, ystart, xstart, ystart);
198             }
199             else
200             {
201               mg.fillRect(xstart, ystart, sameCol, sameRow);
202             }
203
204             xstart = col;
205             sameCol = 1;
206           }
207         }
208         lastrow = (int) (row * sampleRow);
209         ystart = row;
210         sameRow = 1;
211       }
212     }
213   }
214
215   /*
216    * Find the colour of a sequence at a specified column position
217    */
218   private Color getColumnColourFromSequence(
219           jalview.datamodel.SequenceI seq, boolean hiddenRow,
220           boolean hasHiddenCols, int lastcol, FeatureColourFinder finder)
221   {
222     Color color = Color.white;
223     if (seq.getLength() > lastcol)
224     {
225       color = sr.getResidueColour(seq, lastcol, finder);
226     }
227
228     if (hiddenRow
229             || (hasHiddenCols && !av.getColumnSelection()
230                     .isVisible(lastcol)))
231     {
232       color = color.darker().darker();
233     }
234     return color;
235   }
236
237   @Override
238   public void update(Graphics g)
239   {
240     paint(g);
241   }
242
243   @Override
244   public void paint(Graphics g)
245   {
246     Graphics og = offscreen.getGraphics();
247     if (miniMe != null)
248     {
249       og.drawImage(miniMe, 0, 0, this);
250       og.setColor(Color.red);
251       od.drawBox(og);
252       g.drawImage(offscreen, 0, 0, this);
253     }
254   }
255
256 }