3ced9eeaff9e4faf64ac1cd9e5684eaaf2b4363b
[jalview.git] / src / jalview / gui / 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.gui;
22
23 import jalview.api.AlignViewportI;
24 import jalview.datamodel.SequenceI;
25 import jalview.renderer.AnnotationRenderer;
26 import jalview.renderer.seqfeatures.FeatureColourFinder;
27 import jalview.viewmodel.OverviewDimensions;
28
29 import java.awt.Color;
30 import java.awt.Dimension;
31 import java.awt.Graphics;
32 import java.awt.image.BufferedImage;
33
34 import javax.swing.JComponent;
35
36
37 public class OverviewCanvas extends JComponent
38 {
39   private static final Color TRANS_GREY = new Color(100, 100, 100, 25);
40
41   // This is set true if the alignment view changes whilst
42   // the overview is being calculated
43   private volatile boolean restart = false;
44
45   private volatile boolean updaterunning = false;
46
47   private BufferedImage miniMe;
48
49   private BufferedImage lastMiniMe = null;
50
51   // Can set different properties in this seqCanvas than
52   // main visible SeqCanvas
53   private SequenceRenderer sr;
54
55   private jalview.renderer.seqfeatures.FeatureRenderer fr;
56
57   private final AnnotationRenderer renderer = new AnnotationRenderer();
58
59   OverviewDimensions od;
60
61   AlignViewportI av;
62
63   public OverviewCanvas(OverviewDimensions overviewDims,
64           AlignViewportI alignvp)
65   {
66     od = overviewDims;
67     av = alignvp;
68
69     sr = new SequenceRenderer(av);
70     sr.renderGaps = false;
71     sr.forOverview = true;
72     fr = new jalview.renderer.seqfeatures.FeatureRenderer(av);
73   }
74
75   /*
76    * Signals to drawing code that the associated alignment viewport
77    * has changed and a redraw will be required
78    */
79   public boolean restartDraw()
80   {
81     synchronized (this)
82     {
83       if (updaterunning)
84       {
85         restart = true;
86       }
87       else
88       {
89         updaterunning = true;
90       }
91       return restart;
92     }
93   }
94
95   public void draw(boolean showSequenceFeatures, boolean showAnnotation,
96           FeatureRenderer transferRenderer)
97   {
98     miniMe = null;
99
100     if (showSequenceFeatures)
101     {
102       fr.transferSettings(transferRenderer);
103     }
104
105     // why do we need to set preferred size again? was set in
106     // updateOverviewImage
107     setPreferredSize(new Dimension(od.getWidth(), od.getHeight()));
108
109     miniMe = new BufferedImage(od.getWidth(), od.getHeight(),
110             BufferedImage.TYPE_INT_RGB);
111
112     Graphics mg = miniMe.getGraphics();
113     mg.setColor(Color.orange);
114     mg.fillRect(0, 0, od.getWidth(), miniMe.getHeight());
115
116     // calculate sampleCol and sampleRow
117     // alignment width is max number of residues/bases
118     // alignment height is number of sequences
119     int alwidth = av.getAlignment().getWidth();
120     int alheight = av.getAlignment().getAbsoluteHeight();
121
122     // sampleCol or sampleRow is the width/height allocated to each residue
123     // in particular, sometimes we may need more than one row/col of the
124     // BufferedImage allocated
125     // sampleCol is how much of a residue to assign to each pixel
126     // sampleRow is how many sequences to assign to each pixel
127     float sampleCol = alwidth / (float) od.getWidth();
128     float sampleRow = alheight / (float) od.getSequencesHeight();
129
130     buildImage(sampleRow, sampleCol);
131
132     if (showAnnotation)
133     {
134       renderer.updateFromAlignViewport(av);
135       for (int col = 0; col < od.getWidth() && !restart; col++)
136       {
137         mg.translate(col, od.getSequencesHeight());
138         renderer.drawGraph(mg, av.getAlignmentConservationAnnotation(),
139                 av.getAlignmentConservationAnnotation().annotations,
140                 (int) (sampleCol) + 1, od.getGraphHeight(),
141                 (int) (col * sampleCol), (int) (col * sampleCol) + 1);
142         mg.translate(-col, -od.getSequencesHeight());
143
144       }
145     }
146     System.gc();
147
148     if (restart)
149     {
150       restart = false;
151       draw(showSequenceFeatures, showAnnotation, transferRenderer);
152     }
153     else
154     {
155       updaterunning = false;
156       lastMiniMe = miniMe;
157     }
158   }
159
160   @Override
161   public void paintComponent(Graphics g)
162   {
163     if (restart)
164     {
165       if (lastMiniMe == null)
166       {
167         g.setColor(Color.white);
168         g.fillRect(0, 0, getWidth(), getHeight());
169       }
170       else
171       {
172         g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this);
173       }
174       g.setColor(TRANS_GREY);
175       g.fillRect(0, 0, getWidth(), getHeight());
176     }
177     else if (lastMiniMe != null)
178     {
179       g.drawImage(lastMiniMe, 0, 0, this);
180       if (lastMiniMe != miniMe)
181       {
182         g.setColor(TRANS_GREY);
183         g.fillRect(0, 0, getWidth(), getHeight());
184       }
185     }
186
187     g.setColor(Color.red);
188     od.drawBox(g);
189   }
190
191   /*
192    * Build the overview panel image
193    */
194   private void buildImage(float sampleRow, float sampleCol)
195   {
196     int lastcol = -1;
197     int lastrow = -1;
198     int rgbcolor = Color.white.getRGB();
199
200     SequenceI seq = null;
201     FeatureColourFinder finder = new FeatureColourFinder(fr);
202
203     final boolean hasHiddenCols = av.hasHiddenColumns();
204     boolean hiddenRow = false;
205     // get hidden row and hidden column map once at beginning.
206     // clone featureRenderer settings to avoid race conditions... if state is
207     // updated just need to refresh again
208     for (int row = 0; row < od.getSequencesHeight() && !restart; row++)
209     {
210       boolean doCopy = true;
211       int currentrow = (int) (row * sampleRow);
212       if (currentrow != lastrow)
213       {
214         doCopy = false;
215
216         lastrow = currentrow;
217
218         // get the sequence which would be at alignment index 'lastrow' if no
219         // rows were hidden, and determine whether it is hidden or not
220         hiddenRow = av.getAlignment().isHidden(lastrow);
221         seq = av.getAlignment().getSequenceAtAbsoluteIndex(lastrow);
222       }
223
224       for (int col = 0; col < od.getWidth() && !restart; col++)
225       {
226         if (doCopy)
227         {
228           rgbcolor = miniMe.getRGB(col, row - 1);
229         }
230         else if ((int) (col * sampleCol) != lastcol
231                 || (int) (row * sampleRow) != lastrow)
232         {
233           lastcol = (int) (col * sampleCol);
234           rgbcolor = getColumnColourFromSequence(seq, hiddenRow,
235                   hasHiddenCols, lastcol, finder);
236         }
237         // else we just use the color we already have , so don't need to set it
238
239         miniMe.setRGB(col, row, rgbcolor);
240       }
241     }
242   }
243
244   /*
245    * Find the colour of a sequence at a specified column position
246    */
247   private int getColumnColourFromSequence(jalview.datamodel.SequenceI seq,
248           boolean hiddenRow, boolean hasHiddenCols, int lastcol,
249           FeatureColourFinder finder)
250   {
251     Color color = Color.white;
252
253     if ((seq != null) && (seq.getLength() > lastcol))
254     {
255       color = sr.getResidueColour(seq, lastcol, finder);
256     }
257
258     if (hiddenRow
259             || (hasHiddenCols && !av.getColumnSelection()
260                     .isVisible(lastcol)))
261     {
262       color = color.darker().darker();
263     }
264
265     return color.getRGB();
266   }
267
268 }