001package org.biojava.bio.gui.sequence;
002
003import java.awt.Graphics;
004import java.awt.Graphics2D;
005
006import javax.swing.JComponent;
007
008import org.biojava.bio.seq.FeatureHolder;
009import org.biojava.bio.symbol.SymbolList;
010
011/**
012 * Renders a sequence as a circle using a CircularRenderer.
013 *
014 * <p>
015 * This component will first transform the graphic coordinates so that 0,0 is at
016 * the centre of the circle. The size of the circle is estimated from the radius
017 * property and the depth of the renderer.
018 * </p>
019 *
020 * <p>
021 * All angles are measured in radians. Some java gui classes use radians and
022 * some use degrees. Be carefull to use the right one. Math has a couple
023 * of methods for conversions.
024 * </p>
025 *
026 * @author Matthew Pocock
027 * @since 1.4
028 */
029public class CircularRendererPanel
030extends JComponent {
031  private final CircularRendererContext ctxt;
032
033  {
034    ctxt = new CTXT();
035  }
036
037  private SymbolList symList;
038  private double radius;
039  private CircularRenderer renderer;
040  private double offset;
041
042  public double getRadius() {
043    return radius;
044  }
045
046  public void setRadius(double radius) {
047    this.radius = radius;
048  }
049
050  public SymbolList getSequence() {
051    return symList;
052  }
053
054  public void setSequence(SymbolList symList) {
055    this.symList = symList;
056  }
057
058  public double getOffset() {
059    return offset;
060  }
061
062  public void setOffset(double offset) {
063    this.offset = offset;
064  }
065
066  public CircularRenderer getRenderer() {
067    return renderer;
068  }
069
070  public void setRenderer(CircularRenderer renderer) {
071    this.renderer = renderer;
072  }
073
074  public synchronized void paintComponent(Graphics g) {
075    super.paintComponent(g);
076    if(!isActive()) return;
077
078    double depth = renderer.getDepth(ctxt);
079
080    Graphics2D g2 = (Graphics2D) g;
081    g2.translate((depth + radius), (depth + radius));
082
083    renderer.paint(g2, ctxt);
084  }
085
086  private boolean isActive() {
087    return renderer != null;
088  }
089
090  private final class CTXT
091  implements CircularRendererContext {
092    public double getOffset() {
093      return offset;
094    }
095
096    public double getAngle(int indx) {
097      return ((double) indx) * 2.0 * Math.PI / ((double) symList.length()) + offset;
098    }
099
100    public int getIndex(double angle) {
101      return (int) ( (angle - offset) * ((double) symList.length()) / (2.0 * Math.PI));
102    }
103
104    public double getRadius() {
105      return radius;
106    }
107
108    public SymbolList getSymbols() {
109      return symList;
110    }
111
112    public FeatureHolder getFeatures() {
113      if(symList instanceof FeatureHolder) {
114        return (FeatureHolder) symList;
115      } else {
116        return FeatureHolder.EMPTY_FEATURE_HOLDER;
117      }
118    }
119  }
120}