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 */ 021 022package org.biojava.bio.gui.sequence; 023 024import java.awt.Graphics2D; 025import java.awt.Paint; 026import java.awt.Shape; 027import java.awt.Stroke; 028import java.awt.geom.Ellipse2D; 029 030import org.biojava.bio.seq.Feature; 031import org.biojava.bio.symbol.Location; 032import org.biojava.utils.ChangeEvent; 033import org.biojava.utils.ChangeSupport; 034import org.biojava.utils.ChangeType; 035import org.biojava.utils.ChangeVetoException; 036 037/** 038 * <p><code>EllipticalBeadRenderer</code> renders features as simple 039 * ellipses. Their outline and fill <code>Paint</code>, 040 * <code>Stroke</code>, feature depth, Y-axis displacement are 041 * configurable. Also configurable is the minimum ratio of long axis 042 * to short axis of the ellipse - this prevents long features also 043 * becoming ever wider and obscuring neighbours.</p> 044 * 045 * @author Keith James 046 * @since 1.2 047 */ 048public class EllipticalBeadRenderer extends AbstractBeadRenderer 049{ 050 /** 051 * Constant <code>RATIO</code> indicating a change to the minimum 052 * allowed ratio of long axis to short axis of the features. 053 */ 054 public static final ChangeType RATIO = 055 new ChangeType("The shape of the features has changed", 056 "org.biojava.bio.gui.sequence.EllipticalBeadRenderer", 057 "RATIO", SequenceRenderContext.LAYOUT); 058 059 protected double dimensionRatio; 060 061 /** 062 * Creates a new <code>EllipticalBeadRenderer</code> object 063 * with the default settings. 064 */ 065 public EllipticalBeadRenderer() 066 { 067 super(); 068 dimensionRatio = 2.0F; 069 } 070 071 /** 072 * Creates a new <code>EllipticalBeadRenderer</code>. 073 * 074 * @param beadDepth a <code>double</code>. 075 * @param beadDisplacement a <code>double</code>. 076 * @param beadOutline a <code>Paint</code>. 077 * @param beadFill a <code>Paint</code>. 078 * @param beadStroke a <code>Stroke</code>. 079 * @param dimensionRatio a <code>double</code>. 080 */ 081 public EllipticalBeadRenderer(double beadDepth, 082 double beadDisplacement, 083 Paint beadOutline, 084 Paint beadFill, 085 Stroke beadStroke, 086 double dimensionRatio) 087 { 088 super(beadDepth, beadDisplacement, beadOutline, beadFill, beadStroke); 089 dimensionRatio = 2.0F; 090 } 091 092 /** 093 * <code>renderBead</code> renders features as simple ellipse. 094 * 095 * @param g2 a <code>Graphics2D</code> context. 096 * @param f a <code>Feature</code> to render. 097 * @param context a <code>SequenceRenderContext</code> context. 098 */ 099 public void renderBead(Graphics2D g2, 100 Feature f, 101 SequenceRenderContext context) 102 { 103 Location loc = f.getLocation(); 104 105 int min = loc.getMin(); 106 int max = loc.getMax(); 107 int dif = max - min; 108 109 Shape shape; 110 111 if (context.getDirection() == SequenceRenderContext.HORIZONTAL) 112 { 113 double posXW = context.sequenceToGraphics(min); 114 double posYN = beadDisplacement; 115 double width = Math.max(((double) (dif + 1)) * context.getScale(), 1.0f); 116 double height = Math.min(beadDepth, width / dimensionRatio); 117 118 // If the bead height occupies less than the full height 119 // of the renderer, move it down so that it is central 120 if (height < beadDepth) 121 posYN += ((beadDepth - height) / dimensionRatio); 122 123 shape = new Ellipse2D.Double(posXW, posYN, width, height); 124 } 125 else 126 { 127 double posXW = beadDisplacement; 128 double posYN = context.sequenceToGraphics(min); 129 double height = Math.max(((double) dif + 1) * context.getScale(), 1.0f); 130 double width = Math.min(beadDepth, height / dimensionRatio); 131 132 if (width < beadDepth) 133 posXW += ((beadDepth - width) / dimensionRatio); 134 135 shape = new Ellipse2D.Double(posXW, posYN, width, height); 136 } 137 138 g2.setPaint(beadFill); 139 g2.fill(shape); 140 141 g2.setStroke(beadStroke); 142 g2.setPaint(beadOutline); 143 g2.draw(shape); 144 } 145 146 /** 147 * <code>getDepth</code> calculates the depth required by this 148 * renderer to display its beads. 149 * 150 * @param context a <code>SequenceRenderContext</code> object. 151 * 152 * @return a <code>double</code>. 153 */ 154 public double getDepth(SequenceRenderContext context) 155 { 156 // Get max depth of delegates using base class method 157 double maxDepth = super.getDepth(context); 158 return Math.max(maxDepth, (beadDepth + beadDisplacement)); 159 } 160 161 /** 162 * <code>getDimensionRatio</code> returns the maximum ratio of 163 * long dimension to short dimension of the bead. This should be 164 * equal, or greater than 1. 165 * 166 * @return a <code>double</code>. 167 */ 168 public double getDimensionRatio() 169 { 170 return dimensionRatio; 171 } 172 173 /** 174 * <code>setDimensionRatio</code> sets the minimum ratio of 175 * long dimension to short dimension of the bead. This should be 176 * equal, or greater than 1. 177 * 178 * @param ratio a <code>double</code> ratio of depth. 179 * 180 * @exception ChangeVetoException if an error occurs. 181 */ 182 public void setDimensionRatio(double ratio) throws ChangeVetoException 183 { 184 if (ratio < 1.0F) 185 throw new ChangeVetoException("The long dimension may not be less than the short dimension (ratio >= 1.0)"); 186 187 if (hasListeners()) 188 { 189 ChangeSupport cs = getChangeSupport(SequenceRenderContext.LAYOUT); 190 synchronized(cs) 191 { 192 ChangeEvent ce = new ChangeEvent(this, SequenceRenderContext.LAYOUT, 193 null, null, 194 new ChangeEvent(this, RATIO, 195 new Double(dimensionRatio), 196 new Double(ratio))); 197 cs.firePreChangeEvent(ce); 198 dimensionRatio= ratio; 199 cs.firePostChangeEvent(ce); 200 } 201 } 202 else 203 { 204 dimensionRatio = ratio; 205 } 206 } 207}