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.Color; 025import java.awt.Graphics2D; 026import java.awt.Paint; 027import java.awt.event.MouseEvent; 028import java.awt.geom.Line2D; 029import java.awt.geom.Point2D; 030import java.awt.geom.Rectangle2D; 031import java.util.Iterator; 032 033import org.biojava.bio.seq.Feature; 034import org.biojava.bio.seq.FeatureHolder; 035import org.biojava.bio.seq.StrandedFeature; 036import org.biojava.bio.symbol.Location; 037import org.biojava.utils.AbstractChangeable; 038import org.biojava.utils.ChangeEvent; 039import org.biojava.utils.ChangeSupport; 040import org.biojava.utils.ChangeVetoException; 041 042/** 043 * A feature renderer that draws non-contiguous features as a set of boxes 044 * joined by zig-zags. 045 * <p> 046 * This is applicable to rendering cds's or non-contiguous homologies for 047 * example. 048 * 049 * @author Matthew Pocock 050 */ 051public class ZiggyFeatureRenderer 052extends AbstractChangeable 053implements FeatureRenderer, java.io.Serializable { 054 private Paint outline = Color.black; 055 private Paint fill = Color.yellow; 056 private double blockDepth = 10.0; 057 058 public void setFill(Paint p) 059 throws ChangeVetoException { 060 if(hasListeners()) { 061 ChangeSupport cs = getChangeSupport(SequenceRenderContext.REPAINT); 062 synchronized(cs) { 063 ChangeEvent ce = new ChangeEvent( 064 this, SequenceRenderContext.REPAINT, 065 p, this.fill 066 ); 067 cs.firePreChangeEvent(ce); 068 this.fill = p; 069 cs.firePostChangeEvent(ce); 070 } 071 } else { 072 this.fill = p; 073 } 074 } 075 076 public Paint getFill() { 077 return fill; 078 } 079 080 public void setOutline(Paint p) 081 throws ChangeVetoException { 082 if(hasListeners()) { 083 ChangeSupport cs = getChangeSupport(SequenceRenderContext.REPAINT); 084 synchronized(cs) { 085 ChangeEvent ce = new ChangeEvent(this, SequenceRenderContext.REPAINT); 086 cs.firePreChangeEvent(ce); 087 this.outline = p; 088 cs.firePostChangeEvent(ce); 089 } 090 } else { 091 this.outline = p; 092 } 093 } 094 095 public Paint getOutline() { 096 return outline; 097 } 098 099 public void setBlockDepth(double depth) 100 throws ChangeVetoException { 101 if(hasListeners()) { 102 ChangeSupport cs = getChangeSupport(SequenceRenderContext.LAYOUT); 103 synchronized(cs) { 104 ChangeEvent ce = new ChangeEvent(this, SequenceRenderContext.LAYOUT); 105 cs.firePreChangeEvent(ce); 106 this.blockDepth = depth; 107 cs.firePostChangeEvent(ce); 108 } 109 } else { 110 this.blockDepth = depth; 111 } 112 } 113 114 public double getBlockDepth() { 115 return blockDepth; 116 } 117 118 public double getDepth(SequenceRenderContext src) { 119 return blockDepth + 1.0; 120 } 121 122 public void renderFeature( 123 Graphics2D g, Feature f, SequenceRenderContext context 124 ) { 125 Location loc = f.getLocation(); 126 Iterator i = loc.blockIterator(); 127 Location last = null; 128 if(i.hasNext()) { 129 last = (Location) i.next(); 130 renderLocation(g, last, context); 131 } 132 while(i.hasNext()) { 133 Location next = (Location) i.next(); 134 renderLink(g, f, last, next, context); 135 renderLocation(g, next, context); 136 last = next; 137 } 138 } 139 140 private void renderLocation( 141 Graphics2D g, Location loc, SequenceRenderContext context 142 ) { 143 Rectangle2D.Double block = new Rectangle2D.Double(); 144 double min = context.sequenceToGraphics(loc.getMin()); 145 double max = context.sequenceToGraphics(loc.getMax()+1); 146 if(context.getDirection() == SequenceRenderContext.HORIZONTAL) { 147 block.setFrame( 148 min, 0.0, 149 max - min, blockDepth 150 ); 151 } else { 152 block.setFrame( 153 0.0, min, 154 blockDepth, max - min 155 ); 156 } 157 if(fill != null) { 158 g.setPaint(fill); 159 g.fill(block); 160 } 161 if(outline != null) { 162 g.setPaint(outline); 163 g.draw(block); 164 } 165 } 166 167 private StrandedFeature.Strand getStrand(Feature f) { 168 if (f instanceof StrandedFeature) { 169 return ((StrandedFeature) f).getStrand(); 170 } else { 171 FeatureHolder fh = f.getParent(); 172 if (fh instanceof Feature) { 173 return getStrand((Feature) fh); 174 } else { 175 return StrandedFeature.UNKNOWN; 176 } 177 } 178 } 179 180 private void renderLink( 181 Graphics2D g, Feature f, Location source, Location dest, 182 SequenceRenderContext context 183 ) { 184 Line2D line = new Line2D.Double(); 185 Point2D startP; 186 Point2D midP; 187 Point2D endP; 188 double half = blockDepth * 0.5; 189 if(context.getDirection() == SequenceRenderContext.HORIZONTAL) { 190 if(getStrand(f) == StrandedFeature.NEGATIVE) { 191 double start = context.sequenceToGraphics(dest.getMin()); 192 double end = context.sequenceToGraphics(source.getMax()+1); 193 double mid = (start + end) * 0.5; 194 startP = new Point2D.Double(start, half); 195 midP = new Point2D.Double(mid, blockDepth); 196 endP = new Point2D.Double(end, half); 197 } else { 198 double start = context.sequenceToGraphics(source.getMax()+1); 199 double end = context.sequenceToGraphics(dest.getMin()); 200 double mid = (start + end) * 0.5; 201 startP = new Point2D.Double(start, half); 202 midP = new Point2D.Double(mid, 0.0); 203 endP = new Point2D.Double(end, half); 204 } 205 } else { 206 if (getStrand(f) == StrandedFeature.NEGATIVE) { 207 double start = context.sequenceToGraphics(dest.getMin()+1); 208 double end = context.sequenceToGraphics(source.getMax()); 209 double mid = (start + end) * 0.5; 210 startP = new Point2D.Double(half, start); 211 midP = new Point2D.Double(blockDepth, mid); 212 endP = new Point2D.Double(half, end); 213 } else { 214 double start = context.sequenceToGraphics(source.getMax()); 215 double end = context.sequenceToGraphics(dest.getMin()+1); 216 double mid = (start + end) * 0.5; 217 startP = new Point2D.Double(half, start); 218 midP = new Point2D.Double(0.0, mid); 219 endP = new Point2D.Double(half, end); 220 } 221 } 222 g.setPaint(getOutline()); 223 line.setLine(startP, midP); 224 g.draw(line); 225 line.setLine(midP, endP); 226 g.draw(line); 227 } 228 229 public FeatureHolder processMouseEvent( 230 FeatureHolder hits, 231 SequenceRenderContext src, 232 MouseEvent me 233 ) { 234 return hits; 235 } 236}