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.Stroke;
027import java.awt.geom.RoundRectangle2D;
028
029import org.biojava.bio.seq.Feature;
030import org.biojava.bio.symbol.Location;
031
032/**
033 * <code>RoundRectangularBeadRenderer</code> renders features
034 * as rectangles with rounded corners. Their outline and fill
035 * <code>Paint</code>, <code>Stroke</code>, feature depth, Y-axis
036 * displacement are configurable.
037 *
038 * @author Keith James
039 * @since 1.2
040 */
041public class RoundRectangularBeadRenderer extends AbstractBeadRenderer
042{
043    protected RoundRectangle2D rect;
044    protected double arcWidth;
045    protected double arcHeight;
046
047    /**
048     * Creates a new <code>RoundRectangularBeadRenderer</code>
049     * object with the default settings.
050     */
051    public RoundRectangularBeadRenderer()
052    {
053        super();
054        rect = new RoundRectangle2D.Double();
055        arcWidth  = 5.0;
056        arcHeight = 5.0;
057    }
058
059    /**
060     * Creates a new <code>RoundRectangularBeadRenderer</code>.
061     *
062     * @param beadDepth a <code>double</code>.
063     * @param beadDisplacement a <code>double</code>.
064     * @param beadOutline a <code>Paint</code>.
065     * @param beadFill a <code>Paint</code>.
066     * @param beadStroke a <code>Stroke</code>.
067     * @param arcWidth a <code>double</code> value which sets the arc
068     * width of the corners.
069     * @param arcHeight a <code>double</code> value which sets the arc
070     * height of the corners.
071     */
072    public RoundRectangularBeadRenderer(double beadDepth,
073                                        double beadDisplacement,
074                                        Paint  beadOutline,
075                                        Paint  beadFill,
076                                        Stroke beadStroke,
077                                        double arcWidth,
078                                        double arcHeight)
079    {
080        super(beadDepth, beadDisplacement, beadOutline, beadFill, beadStroke);
081        rect = new RoundRectangle2D.Double();
082        this.arcWidth  = arcWidth;
083        this.arcHeight = arcHeight;
084    }
085
086    /**
087     * <code>renderBead</code> renders features as a rectangle with
088     * rounded corners.
089     *
090     * @param g2 a <code>Graphics2D</code>.
091     * @param f a <code>Feature</code> to render.
092     * @param context a <code>SequenceRenderContext</code> context.
093     */
094    public void renderBead(Graphics2D            g2,
095                           Feature               f,
096                           SequenceRenderContext context)
097    {
098        Location loc = f.getLocation();
099
100        int min = loc.getMin();
101        int max = loc.getMax();
102        int dif = max - min;
103
104        if (context.getDirection() == SequenceRenderContext.HORIZONTAL)
105        {
106            double  posXW = context.sequenceToGraphics(min);
107            double  posYN = beadDisplacement;
108            double  width = Math.max(((double) (dif + 1)) * context.getScale(), 1.0);
109            double height = Math.min(beadDepth, width / 2.0);
110
111            // If the bead height occupies less than the full height
112            // of the renderer, move it down so that it is central
113            if (height < beadDepth)
114                posYN += ((beadDepth - height) / 2.0);
115
116            rect.setRoundRect(posXW, posYN,
117                              Math.floor(width),
118                              Math.floor(height),
119                              arcWidth, arcHeight);
120        }
121        else
122        {
123            double  posXW = beadDisplacement;
124            double  posYN = context.sequenceToGraphics(min);
125            double height = Math.max(((double) dif + 1) * context.getScale(), 1.0);
126            double  width = Math.min(beadDepth, height / 2.0);
127
128            if (width < beadDepth)
129                posXW += ((beadDepth - width) /  2.0);
130
131            rect.setRoundRect(posXW, posYN,
132                              Math.floor(width),
133                              Math.floor(height),
134                              arcWidth, arcHeight);
135        }
136
137        g2.setPaint(beadFill);
138        g2.fill(rect);
139
140        g2.setStroke(beadStroke);
141        g2.setPaint(beadOutline);
142        g2.draw(rect);
143    }
144
145    /**
146     * <code>getDepth</code> calculates the depth required by this
147     * renderer to display its beads.
148     *
149     * @param context a <code>SequenceRenderContext</code>.
150     *
151     * @return a <code>double</code>.
152     */
153    public double getDepth(SequenceRenderContext context)
154    {
155        // Get max depth of delegates using base class method
156        double maxDepth = super.getDepth(context);
157        return Math.max(maxDepth, (beadDepth + beadDisplacement));
158    }
159}