/* * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) * Copyright (C) $$Year-Rel$$ The Jalview Authors * * This file is part of Jalview. * * Jalview is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * Jalview is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Jalview. If not, see . * The Jalview Authors are detailed in the 'AUTHORS' file. */ package jalview.schemes; import java.awt.Color; import java.util.IdentityHashMap; import java.util.Map; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.AnnotatedCollectionI; import jalview.datamodel.Annotation; import jalview.datamodel.GraphLine; import jalview.datamodel.SequenceCollectionI; import jalview.datamodel.SequenceI; public class AnnotationColourGradient extends FollowerColourScheme { public static final int NO_THRESHOLD = -1; public static final int BELOW_THRESHOLD = 0; public static final int ABOVE_THRESHOLD = 1; private final AlignmentAnnotation annotation; private final int aboveAnnotationThreshold; private boolean thresholdIsMinMax = false; private GraphLine annotationThreshold; private int redMin; private int greenMin; private int blueMin; private int redRange; private int greenRange; private int blueRange; private boolean predefinedColours = false; private boolean seqAssociated = false; /** * false if the scheme was constructed without a minColour and maxColour used * to decide if existing colours should be taken from annotation elements when * they exist */ private boolean noGradient = false; private IdentityHashMap seqannot = null; @Override public ColourSchemeI applyTo(AnnotatedCollectionI sg, Map hiddenRepSequences) { AnnotationColourGradient acg = new AnnotationColourGradient(annotation, colourScheme, aboveAnnotationThreshold); acg.thresholdIsMinMax = thresholdIsMinMax; acg.annotationThreshold = (annotationThreshold == null) ? null : new GraphLine(annotationThreshold); acg.redMin = redMin; acg.greenMin = greenMin; acg.blueMin = blueMin; acg.redRange = redRange; acg.greenRange = greenRange; acg.blueRange = blueRange; acg.predefinedColours = predefinedColours; acg.seqAssociated = seqAssociated; acg.noGradient = noGradient; return acg; } /** * Creates a new AnnotationColourGradient object. */ public AnnotationColourGradient(AlignmentAnnotation annotation, ColourSchemeI originalColour, int aboveThreshold) { if (originalColour instanceof AnnotationColourGradient) { colourScheme = ((AnnotationColourGradient) originalColour).colourScheme; } else { colourScheme = originalColour; } this.annotation = annotation; aboveAnnotationThreshold = aboveThreshold; if (aboveThreshold != NO_THRESHOLD && annotation.threshold != null) { annotationThreshold = annotation.threshold; } // clear values so we don't get weird black bands... redMin = 254; greenMin = 254; blueMin = 254; redRange = 0; greenRange = 0; blueRange = 0; noGradient = true; checkLimits(); } /** * Creates a new AnnotationColourGradient object. */ public AnnotationColourGradient(AlignmentAnnotation annotation, Color minColour, Color maxColour, int aboveThreshold) { this.annotation = annotation; aboveAnnotationThreshold = aboveThreshold; if (aboveThreshold != NO_THRESHOLD && annotation.threshold != null) { annotationThreshold = annotation.threshold; } redMin = minColour.getRed(); greenMin = minColour.getGreen(); blueMin = minColour.getBlue(); redRange = maxColour.getRed() - redMin; greenRange = maxColour.getGreen() - greenMin; blueRange = maxColour.getBlue() - blueMin; noGradient = false; checkLimits(); } private void checkLimits() { aamax = annotation.graphMax; aamin = annotation.graphMin; if (annotation.isRNA()) { // reset colour palette ColourSchemeProperty.resetRnaHelicesShading(); ColourSchemeProperty.initRnaHelicesShading(1 + (int) aamax); } } @Override public void alignmentChanged(AnnotatedCollectionI alignment, Map hiddenReps) { super.alignmentChanged(alignment, hiddenReps); if (seqAssociated && annotation.getCalcId() != null) { if (seqannot != null) { seqannot.clear(); } else { seqannot = new IdentityHashMap(); } // resolve the context containing all the annotation for the sequence AnnotatedCollectionI alcontext = alignment instanceof AlignmentI ? alignment : alignment.getContext(); boolean f = true, rna = false; for (AlignmentAnnotation alan : alcontext.findAnnotation(annotation .getCalcId())) { if (alan.sequenceRef != null && (alan.label != null && annotation != null && alan.label .equals(annotation.label))) { if (!rna && alan.isRNA()) { rna = true; } seqannot.put(alan.sequenceRef, alan); if (f || alan.graphMax > aamax) { aamax = alan.graphMax; } if (f || alan.graphMin < aamin) { aamin = alan.graphMin; } f = false; } } if (rna) { ColourSchemeProperty.initRnaHelicesShading(1 + (int) aamax); } } } float aamin = 0f, aamax = 0f; public AlignmentAnnotation getAnnotation() { return annotation; } public int getAboveThreshold() { return aboveAnnotationThreshold; } public float getAnnotationThreshold() { if (annotationThreshold == null) { return 0; } else { return annotationThreshold.value; } } public Color getMinColour() { return new Color(redMin, greenMin, blueMin); } public Color getMaxColour() { return new Color(redMin + redRange, greenMin + greenRange, blueMin + blueRange); } /** * DOCUMENT ME! * * @param n * DOCUMENT ME! * * @return DOCUMENT ME! */ @Override public Color findColour(char c) { return Color.red; } /** * DOCUMENT ME! * * @param n * DOCUMENT ME! * @param j * DOCUMENT ME! * * @return DOCUMENT ME! */ @Override public Color findColour(char c, int j, SequenceI seq) { Color currentColour = Color.white; AlignmentAnnotation ann = (seqAssociated && seqannot != null ? seqannot .get(seq) : this.annotation); if (ann == null) { return currentColour; } if ((threshold == 0) || aboveThreshold(c, j)) { if (ann.annotations != null && j < ann.annotations.length && ann.annotations[j] != null && !jalview.util.Comparison.isGap(c)) { Annotation aj = ann.annotations[j]; // 'use original colours' => colourScheme != null // -> look up colour to be used // predefined colours => preconfigured shading // -> only use original colours reference if thresholding enabled & // minmax exists // annotation.hasIcons => null or black colours replaced with glyph // colours // -> reuse original colours if present // -> if thresholding enabled then return colour on non-whitespace glyph if (aboveAnnotationThreshold == NO_THRESHOLD || (annotationThreshold != null && (aboveAnnotationThreshold == ABOVE_THRESHOLD ? aj.value >= annotationThreshold.value : aj.value <= annotationThreshold.value))) { if (predefinedColours && aj.colour != null && !aj.colour.equals(Color.black)) { currentColour = aj.colour; } else if (ann.hasIcons && ann.graph == AlignmentAnnotation.NO_GRAPH) { if (aj.secondaryStructure > ' ' && aj.secondaryStructure != '.' && aj.secondaryStructure != '-') { if (colourScheme != null) { currentColour = colourScheme.findColour(c, j, seq); } else { if (ann.isRNA()) { currentColour = ColourSchemeProperty.rnaHelices[(int) aj.value]; } else { currentColour = ann.annotations[j].secondaryStructure == 'H' ? jalview.renderer.AnnotationRenderer.HELIX_COLOUR : ann.annotations[j].secondaryStructure == 'E' ? jalview.renderer.AnnotationRenderer.SHEET_COLOUR : jalview.renderer.AnnotationRenderer.STEM_COLOUR; } } } else { // return Color.white; } } else if (noGradient) { if (colourScheme != null) { currentColour = colourScheme.findColour(c, j, seq); } else { if (aj.colour != null) { currentColour = aj.colour; } } } else { currentColour = shadeCalculation(ann, j); } } if (conservationColouring) { currentColour = applyConservation(currentColour, j); } } } return currentColour; } /** * Returns a graduated colour for the annotation at the given column. If there * is a threshold value, and it is used as the top/bottom of the colour range, * and the value satisfies the threshold condition, then a colour * proportionate to the range from the threshold is calculated. For all other * cases, a colour proportionate to the annotation's min-max range is * calulated. Note that thresholding is _not_ done here (a colour is computed * even if threshold is not passed), but by the renderer. * * @param ann * @param col * @return */ Color shadeCalculation(AlignmentAnnotation ann, int col) { float range = 1f; float value = ann.annotations[col].value; if (thresholdIsMinMax && ann.threshold != null && aboveAnnotationThreshold == ABOVE_THRESHOLD && value >= ann.threshold.value) { range = (value - ann.threshold.value) / (ann.graphMax - ann.threshold.value); } else if (thresholdIsMinMax && ann.threshold != null && aboveAnnotationThreshold == BELOW_THRESHOLD && value <= ann.threshold.value) { range = (value - ann.graphMin) / (ann.threshold.value - ann.graphMin); } else { if (ann.graphMax != ann.graphMin) { range = (value - ann.graphMin) / (ann.graphMax - ann.graphMin); } else { range = 0f; } } int dr = (int) (redRange * range + redMin); int dg = (int) (greenRange * range + greenMin); int db = (int) (blueRange * range + blueMin); return new Color(dr, dg, db); } public boolean isPredefinedColours() { return predefinedColours; } public void setPredefinedColours(boolean predefinedColours) { this.predefinedColours = predefinedColours; } public boolean isSeqAssociated() { return seqAssociated; } public void setSeqAssociated(boolean sassoc) { seqAssociated = sassoc; } public boolean isThresholdIsMinMax() { return thresholdIsMinMax; } public void setThresholdIsMinMax(boolean thresholdIsMinMax) { this.thresholdIsMinMax = thresholdIsMinMax; } }