- return initialCol;
- }
-
- if (seq != lastSeq)
- {
- lastSeq = seq;
- sequenceFeatures = lastSeq.getDatasetSequence().getSequenceFeatures();
- if (sequenceFeatures != null)
- {
- sfSize = sequenceFeatures.length;
- }
- }
-
- if (sequenceFeatures != lastSeq.getDatasetSequence()
- .getSequenceFeatures())
- {
- sequenceFeatures = lastSeq.getDatasetSequence().getSequenceFeatures();
- if (sequenceFeatures != null)
- {
- sfSize = sequenceFeatures.length;
- }
- }
-
- if (sequenceFeatures == null || sfSize == 0)
- {
- return initialCol;
- }
-
- if (jalview.util.Comparison.isGap(lastSeq.getCharAt(column)))
- {
- return Color.white.getRGB();
- }
-
- // Only bother making an offscreen image if transparency is applied
- if (transparency != 1.0f && offscreenImage == null)
- {
- offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
- }
-
- currentColour = null;
- // TODO: non-threadsafe - each rendering thread needs its own instance of
- // the feature renderer - or this should be synchronized.
- offscreenRender = true;
-
- if (offscreenImage != null)
- {
- offscreenImage.setRGB(0, 0, initialCol);
- drawSequence(offscreenImage.getGraphics(), lastSeq, column, column, 0);
-
- return offscreenImage.getRGB(0, 0);
- }
- else
- {
- drawSequence(null, lastSeq, lastSeq.findPosition(column), -1, -1);
-
- if (currentColour == null)
- {
- return initialCol;
- }
- else
- {
- return ((Integer) currentColour).intValue();
- }
- }
-
- }
-
- /**
- * DOCUMENT ME!
- *
- * @param g
- * DOCUMENT ME!
- * @param seq
- * DOCUMENT ME!
- * @param sg
- * DOCUMENT ME!
- * @param start
- * DOCUMENT ME!
- * @param end
- * DOCUMENT ME!
- * @param x1
- * DOCUMENT ME!
- * @param y1
- * DOCUMENT ME!
- * @param width
- * DOCUMENT ME!
- * @param height
- * DOCUMENT ME!
- */
- // String type;
- // SequenceFeature sf;
- SequenceI lastSeq;
-
- SequenceFeature[] sequenceFeatures;
-
- int sfSize, sfindex, spos, epos;
-
- /**
- * show scores as heights
- */
- protected boolean varyHeight = false;
-
- synchronized public void drawSequence(Graphics g, SequenceI seq,
- int start, int end, int y1)
- {
-
- if (seq.getDatasetSequence().getSequenceFeatures() == null
- || seq.getDatasetSequence().getSequenceFeatures().length == 0)
- {
- return;
- }
-
- if (g != null)
- {
- fm = g.getFontMetrics();
- }
-
- if (av.featuresDisplayed == null || renderOrder == null
- || newFeatureAdded)
- {
- findAllFeatures();
- if (av.featuresDisplayed.size() < 1)
- {
- return;
- }
-
- sequenceFeatures = seq.getDatasetSequence().getSequenceFeatures();
- }
-
- if (lastSeq == null
- || seq != lastSeq
- || seq.getDatasetSequence().getSequenceFeatures() != sequenceFeatures)
- {
- lastSeq = seq;
- sequenceFeatures = seq.getDatasetSequence().getSequenceFeatures();
- }
-
- if (transparency != 1 && g != null)
- {
- Graphics2D g2 = (Graphics2D) g;
- g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
- transparency));
- }
-
- if (!offscreenRender)
- {
- spos = lastSeq.findPosition(start);
- epos = lastSeq.findPosition(end);
- }
-
- sfSize = sequenceFeatures.length;
- String type;
- for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
- {
- type = renderOrder[renderIndex];
-
- if (type == null || !av.featuresDisplayed.containsKey(type))
- {
- continue;
- }
-
- // loop through all features in sequence to find
- // current feature to render
- for (sfindex = 0; sfindex < sfSize; sfindex++)
- {
- if (!sequenceFeatures[sfindex].type.equals(type))
- {
- continue;
- }
-
- if (featureGroups != null
- && sequenceFeatures[sfindex].featureGroup != null
- && sequenceFeatures[sfindex].featureGroup.length() != 0
- && featureGroups
- .containsKey(sequenceFeatures[sfindex].featureGroup)
- && !((Boolean) featureGroups
- .get(sequenceFeatures[sfindex].featureGroup))
- .booleanValue())
- {
- continue;
- }
-
- if (!offscreenRender
- && (sequenceFeatures[sfindex].getBegin() > epos || sequenceFeatures[sfindex]
- .getEnd() < spos))
- {
- continue;
- }
-
- if (offscreenRender && offscreenImage == null)
- {
- if (sequenceFeatures[sfindex].begin <= start
- && sequenceFeatures[sfindex].end >= start)
- {
- // this is passed out to the overview and other sequence renderers
- // (e.g. molecule viewer) to get displayed colour for rendered
- // sequence
- currentColour = new Integer(
- getColour(sequenceFeatures[sfindex]).getRGB());
- // used to be retreived from av.featuresDisplayed
- // currentColour = av.featuresDisplayed
- // .get(sequenceFeatures[sfindex].type);
-
- }
- }
- else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))
- {
-
- renderFeature(g, seq,
- seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
- seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
- getColour(sequenceFeatures[sfindex])
- // new Color(((Integer) av.featuresDisplayed
- // .get(sequenceFeatures[sfindex].type)).intValue())
- , start, end, y1);
- renderFeature(g, seq,
- seq.findIndex(sequenceFeatures[sfindex].end) - 1,
- seq.findIndex(sequenceFeatures[sfindex].end) - 1,
- getColour(sequenceFeatures[sfindex])
- // new Color(((Integer) av.featuresDisplayed
- // .get(sequenceFeatures[sfindex].type)).intValue())
- , start, end, y1);
-
- }
- else if (showFeature(sequenceFeatures[sfindex]))
- {
- if (av.showSeqFeaturesHeight
- && sequenceFeatures[sfindex].score != Float.NaN)
- {
- renderScoreFeature(g, seq,
- seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
- seq.findIndex(sequenceFeatures[sfindex].end) - 1,
- getColour(sequenceFeatures[sfindex]), start, end, y1,
- normaliseScore(sequenceFeatures[sfindex]));
- }
- else
- {
- renderFeature(g, seq,
- seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
- seq.findIndex(sequenceFeatures[sfindex].end) - 1,
- getColour(sequenceFeatures[sfindex]), start, end, y1);
- }
- }
-
- }
-
- }
-
- if (transparency != 1.0f && g != null)
- {
- Graphics2D g2 = (Graphics2D) g;
- g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
- 1.0f));
- }
- }
-
- Hashtable minmax = new Hashtable();
-
- /**
- * normalise a score against the max/min bounds for the feature type.
- *
- * @param sequenceFeature
- * @return byte[] { signed, normalised signed (-127 to 127) or unsigned
- * (0-255) value.
- */
- private final byte[] normaliseScore(SequenceFeature sequenceFeature)
- {
- float[] mm = ((float[][]) minmax.get(sequenceFeature.type))[0];
- final byte[] r = new byte[]
- { 0, (byte) 255 };
- if (mm != null)
- {
- if (r[0] != 0 || mm[0] < 0.0)
- {
- r[0] = 1;
- r[1] = (byte) ((int) 128.0 + 127.0 * (sequenceFeature.score / mm[1]));
- }
- else
- {
- r[1] = (byte) ((int) 255.0 * (sequenceFeature.score / mm[1]));
- }
- }
- return r;
- }
-
- char s;
-
- int i;
-
- void renderFeature(Graphics g, SequenceI seq, int fstart, int fend,
- Color featureColour, int start, int end, int y1)
- {
-
- if (((fstart <= end) && (fend >= start)))
- {
- if (fstart < start)
- { // fix for if the feature we have starts before the sequence start,
- fstart = start; // but the feature end is still valid!!
- }
-
- if (fend >= end)
- {
- fend = end;
- }
- int pady = (y1 + av.charHeight) - av.charHeight / 5;
- for (i = fstart; i <= fend; i++)
- {
- s = seq.getCharAt(i);
-
- if (jalview.util.Comparison.isGap(s))
- {
- continue;
- }
-
- g.setColor(featureColour);
-
- g.fillRect((i - start) * av.charWidth, y1, av.charWidth,
- av.charHeight);
-
- if (offscreenRender || !av.validCharWidth)
- {
- continue;
- }
-
- g.setColor(Color.white);
- charOffset = (av.charWidth - fm.charWidth(s)) / 2;
- g.drawString(String.valueOf(s), charOffset
- + (av.charWidth * (i - start)), pady);
-
- }
- }
- }
-
- void renderScoreFeature(Graphics g, SequenceI seq, int fstart, int fend,
- Color featureColour, int start, int end, int y1, byte[] bs)
- {
-
- if (((fstart <= end) && (fend >= start)))
- {
- if (fstart < start)
- { // fix for if the feature we have starts before the sequence start,
- fstart = start; // but the feature end is still valid!!
- }
-
- if (fend >= end)
- {
- fend = end;
- }
- int pady = (y1 + av.charHeight) - av.charHeight / 5;
- int ystrt = 0, yend = av.charHeight;
- if (bs[0] != 0)
- {
- // signed - zero is always middle of residue line.
- if (bs[1] < 128)
- {
- yend = av.charHeight * (128 - bs[1]) / 512;
- ystrt = av.charHeight - yend / 2;
- }
- else
- {
- ystrt = av.charHeight / 2;
- yend = av.charHeight * (bs[1] - 128) / 512;
- }
- }
- else
- {
- yend = av.charHeight * bs[1] / 255;
- ystrt = av.charHeight - yend;
-
- }
- for (i = fstart; i <= fend; i++)
- {
- s = seq.getCharAt(i);
-
- if (jalview.util.Comparison.isGap(s))
- {
- continue;
- }
-
- g.setColor(featureColour);
- int x = (i - start) * av.charWidth;
- g.drawRect(x, y1, av.charWidth, av.charHeight);
- g.fillRect(x, y1 + ystrt, av.charWidth, yend);
-
- if (offscreenRender || !av.validCharWidth)
- {
- continue;
- }
-
- g.setColor(Color.black);
- charOffset = (av.charWidth - fm.charWidth(s)) / 2;
- g.drawString(String.valueOf(s), charOffset
- + (av.charWidth * (i - start)), pady);
-
- }
- }
- }
-
- boolean newFeatureAdded = false;
-
- /**
- * Called when alignment in associated view has new/modified features to
- * discover and display.
- *
- */
- public void featuresAdded()
- {
- lastSeq = null;
- findAllFeatures();
- }
-
- boolean findingFeatures = false;
-
- /**
- * search the alignment for all new features, give them a colour and display
- * them. Then fires a PropertyChangeEvent on the changeSupport object.
- *
- */
- void findAllFeatures()
- {
- synchronized (firing)
- {
- if (firing.equals(Boolean.FALSE))
- {
- firing = Boolean.TRUE;
- findAllFeatures(true); // add all new features as visible
- changeSupport.firePropertyChange("changeSupport", null, null);
- firing = Boolean.FALSE;
- }
- }
- }
-
- /**
- * Searches alignment for all features and updates colours
- *
- * @param newMadeVisible
- * if true newly added feature types will be rendered immediatly
- */
- synchronized void findAllFeatures(boolean newMadeVisible)
- {
- newFeatureAdded = false;
-
- if (findingFeatures)
- {
- newFeatureAdded = true;
- return;
- }
-
- findingFeatures = true;
-
- if (av.featuresDisplayed == null)
- {
- av.featuresDisplayed = new Hashtable();
- }
-
- allfeatures = new Vector();
- Vector oldfeatures = new Vector();
- if (renderOrder != null)
- {
- for (int i = 0; i < renderOrder.length; i++)
- {
- if (renderOrder[i] != null)
- {
- oldfeatures.addElement(renderOrder[i]);
- }
- }
- }
- if (minmax == null)
- {
- minmax = new Hashtable();
- }
- AlignmentI alignment = av.getAlignment();
- for (int i = 0; i < alignment.getHeight(); i++)
- {
- SequenceFeature[] features = alignment.getSequenceAt(i)
- .getDatasetSequence().getSequenceFeatures();
-
- if (features == null)
- {
- continue;
- }
-
- int index = 0;
- while (index < features.length)
- {
- if (!av.featuresDisplayed.containsKey(features[index].getType()))
- {
-
- if (featureGroups.containsKey(features[index].getType()))
- {
- boolean visible = ((Boolean) featureGroups
- .get(features[index].featureGroup)).booleanValue();
-
- if (!visible)
- {
- index++;
- continue;
- }
- }
-
- if (!(features[index].begin == 0 && features[index].end == 0))
- {
- // If beginning and end are 0, the feature is for the whole sequence
- // and we don't want to render the feature in the normal way
-
- if (newMadeVisible
- && !oldfeatures.contains(features[index].getType()))
- {
- // this is a new feature type on the alignment. Mark it for
- // display.
- av.featuresDisplayed.put(features[index].getType(),
- new Integer(getColour(features[index].getType())
- .getRGB()));
- setOrder(features[index].getType(), 0);
- }
- }
- }
- if (!allfeatures.contains(features[index].getType()))
- {
- allfeatures.addElement(features[index].getType());
- }
- if (features[index].score != Float.NaN)
- {
- int nonpos = features[index].getBegin() >= 1 ? 0 : 1;
- float[][] mm = (float[][]) minmax.get(features[index].getType());
- if (mm == null)
- {
- mm = new float[][]
- { null, null };
- minmax.put(features[index].getType(), mm);
- }
- if (mm[nonpos] == null)
- {
- mm[nonpos] = new float[]
- { features[index].score, features[index].score };
-
- }
- else
- {
- if (mm[nonpos][0] > features[index].score)
- {
- mm[nonpos][0] = features[index].score;
- }
- if (mm[nonpos][1] < features[index].score)
- {
- mm[nonpos][1] = features[index].score;
- }
- }
- }
- index++;
- }
- }
- updateRenderOrder(allfeatures);
- findingFeatures = false;
- }
-
- protected Boolean firing = Boolean.FALSE;
-
- /**
- * replaces the current renderOrder with the unordered features in
- * allfeatures. The ordering of any types in both renderOrder and allfeatures
- * is preserved, and all new feature types are rendered on top of the existing
- * types, in the order given by getOrder or the order given in allFeatures.
- * Note. this operates directly on the featureOrder hash for efficiency. TODO:
- * eliminate the float storage for computing/recalling the persistent ordering
- * New Cability: updates min/max for colourscheme range if its dynamic
- *
- * @param allFeatures
- */
- private void updateRenderOrder(Vector allFeatures)
- {
- Vector allfeatures = new Vector(allFeatures);
- String[] oldRender = renderOrder;
- renderOrder = new String[allfeatures.size()];
- Object mmrange, fc = null;
- boolean initOrders = (featureOrder == null);
- int opos = 0;
- if (oldRender != null && oldRender.length > 0)
- {
- for (int j = 0; j < oldRender.length; j++)
- {
- if (oldRender[j] != null)
- {
- if (initOrders)
- {
- setOrder(oldRender[j], (1 - (1 + (float) j)
- / (float) oldRender.length));
- }
- if (allfeatures.contains(oldRender[j]))
- {
- renderOrder[opos++] = oldRender[j]; // existing features always
- // appear below new features
- allfeatures.removeElement(oldRender[j]);
- if (minmax != null)
- {
- mmrange = minmax.get(oldRender[j]);
- if (mmrange != null)
- {
- fc = featureColours.get(oldRender[j]);
- if (fc != null && fc instanceof GraduatedColor
- && ((GraduatedColor) fc).isAutoScale())
- {
- ((GraduatedColor) fc).updateBounds(
- ((float[][]) mmrange)[0][0],
- ((float[][]) mmrange)[0][1]);
- }
- }
- }
- }
- }
- }
- }
- if (allfeatures.size() == 0)
- {
- // no new features - leave order unchanged.
- return;
- }
- int i = allfeatures.size() - 1;
- int iSize = i;
- boolean sort = false;
- String[] newf = new String[allfeatures.size()];
- float[] sortOrder = new float[allfeatures.size()];
- Enumeration en = allfeatures.elements();
- // sort remaining elements
- while (en.hasMoreElements())
- {
- newf[i] = en.nextElement().toString();
- if (minmax != null)
- {
- // update from new features minmax if necessary
- mmrange = minmax.get(newf[i]);
- if (mmrange != null)
- {
- fc = featureColours.get(newf[i]);
- if (fc != null && fc instanceof GraduatedColor
- && ((GraduatedColor) fc).isAutoScale())
- {
- ((GraduatedColor) fc).updateBounds(((float[][]) mmrange)[0][0],
- ((float[][]) mmrange)[0][1]);
- }
- }
- }
- if (initOrders || !featureOrder.containsKey(newf[i]))
- {
- int denom = initOrders ? allfeatures.size() : featureOrder.size();
- // new unordered feature - compute persistent ordering at head of
- // existing features.
- setOrder(newf[i], i / (float) denom);
- }
- // set order from newly found feature from persisted ordering.
- sortOrder[i] = 2 - ((Float) featureOrder.get(newf[i])).floatValue();
- if (i < iSize)
- {
- // only sort if we need to
- sort = sort || sortOrder[i] > sortOrder[i + 1];
- }
- i--;
- }
- if (iSize > 1 && sort)
- {
- jalview.util.QuickSort.sort(sortOrder, newf);
- }
- sortOrder = null;
- System.arraycopy(newf, 0, renderOrder, opos, newf.length);
- }
-
- /**
- * get a feature style object for the given type string. Creates a
- * java.awt.Color for a featureType with no existing colourscheme. TODO:
- * replace return type with object implementing standard abstract colour/style
- * interface
- *
- * @param featureType
- * @return java.awt.Color or GraduatedColor
- */
- public Object getFeatureStyle(String featureType)
- {
- Object fc = featureColours.get(featureType);
- if (fc == null)
- {
- jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme();
- Color col = ucs.createColourFromName(featureType);
- featureColours.put(featureType, fc = col);
- }
- return fc;
- }
-
- /**
- * return a nominal colour for this feature
- *
- * @param featureType
- * @return standard color, or maximum colour for graduated colourscheme
- */
- public Color getColour(String featureType)
- {
- Object fc = getFeatureStyle(featureType);
-
- if (fc instanceof Color)
- {
- return (Color) fc;
- }
- else
- {
- if (fc instanceof GraduatedColor)
- {
- return ((GraduatedColor) fc).getMaxColor();
- }
- }
- throw new Error("Implementation Error: Unrecognised render object "
- + fc.getClass() + " for features of type " + featureType);
- }
-
- /**
- * calculate the render colour for a specific feature using current feature
- * settings.
- *
- * @param feature
- * @return render colour for the given feature
- */
- public Color getColour(SequenceFeature feature)
- {
- Object fc = getFeatureStyle(feature.getType());
- if (fc instanceof Color)
- {
- return (Color) fc;
- }
- else
- {
- if (fc instanceof GraduatedColor)
- {
- return ((GraduatedColor) fc).findColor(feature);
- }
- }
- throw new Error("Implementation Error: Unrecognised render object "
- + fc.getClass() + " for features of type " + feature.getType());
- }
-
- private boolean showFeature(SequenceFeature sequenceFeature)
- {
- Object fc = getFeatureStyle(sequenceFeature.type);
- if (fc instanceof GraduatedColor)
- {
- return ((GraduatedColor) fc).isColored(sequenceFeature);
- }
- else
- {
- return true;