Bouncer.java Source code

Java tutorial

Introduction

Here is the source code for Bouncer.java

Source

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Checkbox;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Label;
import java.awt.Panel;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import java.text.NumberFormat;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Bouncer extends JPanel implements Runnable {
    private boolean trucking = true;

    private long[] previousTimes; // milliseconds

    private int previousIndex;

    private boolean previousFilled;

    private double frameRate; // frames per second

    private Image image;

    public static void main(String[] args) {
        final Bouncer bouncer = new Bouncer();
        Frame f = new AnimationFrame(bouncer);
        f.setFont(new Font("Serif", Font.PLAIN, 12));
        f.setSize(200, 200);
        Panel controls = new Panel();
        controls.add(bouncer.createCheckbox("Anti.", Bouncer.ANTIALIASING));
        controls.add(bouncer.createCheckbox("Trans.", Bouncer.TRANSFORM));
        controls.add(bouncer.createCheckbox("Gradient", Bouncer.GRADIENT));
        controls.add(bouncer.createCheckbox("Outline", Bouncer.OUTLINE));
        controls.add(bouncer.createCheckbox("Dotted", Bouncer.DOTTED));
        controls.add(bouncer.createCheckbox("Axes", Bouncer.AXES));
        controls.add(bouncer.createCheckbox("Clip", Bouncer.CLIP));
        f.add(controls, BorderLayout.NORTH);

        f.setVisible(true);
    }

    // Tweakable variables
    private boolean mAntialiasing, mGradient, mOutline;

    private boolean mTransform, mDotted, mAxes, mClip;

    // ...and the constants that represent them. See setSwitch().
    public static final int ANTIALIASING = 0;

    public static final int GRADIENT = 1;

    public static final int OUTLINE = 2;

    public static final int TRANSFORM = 3;

    public static final int DOTTED = 4;

    public static final int AXES = 5;

    public static final int CLIP = 6;

    private float[] mPoints;

    private float[] mDeltas;

    private float mTheta;

    private int mN;

    private Shape mClipShape;

    public Bouncer() {
        previousTimes = new long[128];
        previousTimes[0] = System.currentTimeMillis();
        previousIndex = 1;
        previousFilled = false;

        mN = 38;
        mPoints = new float[mN];
        mDeltas = new float[mN];
        Random random = new Random();
        for (int i = 0; i < mN; i++) {
            mPoints[i] = random.nextFloat() * 500;
            mDeltas[i] = random.nextFloat() * 3;
        }

        addComponentListener(new ComponentAdapter() {
            public void componentResized(ComponentEvent ce) {
                Dimension d = getSize();
                for (int i = 0; i < mN; i++) {
                    int limit = ((i % 2) == 0) ? d.width : d.height;
                    if (mPoints[i] < 0)
                        mPoints[i] = 0;
                    else if (mPoints[i] >= limit)
                        mPoints[i] = limit - 1;
                }
            }
        });
    }

    public void setSwitch(int item, boolean value) {
        switch (item) {
        case ANTIALIASING:
            mAntialiasing = value;
            break;
        case GRADIENT:
            mGradient = value;
            break;
        case OUTLINE:
            mOutline = value;
            break;
        case TRANSFORM:
            mTransform = value;
            break;
        case DOTTED:
            mDotted = value;
            break;
        case AXES:
            mAxes = value;
            break;
        case CLIP:
            mClip = value;
            break;
        default:
            break;
        }
    }

    protected Checkbox createCheckbox(String label, final int item) {
        Checkbox check = new Checkbox(label);
        check.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent ie) {
                setSwitch(item, (ie.getStateChange() == ie.SELECTED));
            }
        });
        return check;
    }

    public void timeStep() {
        Dimension d = getSize();
        for (int i = 0; i < mN; i++) {
            float value = mPoints[i] + mDeltas[i];
            int limit = ((i % 2) == 0) ? d.width : d.height;
            if (value < 0 || value > limit) {
                mDeltas[i] = -mDeltas[i];
                mPoints[i] += mDeltas[i];
            } else
                mPoints[i] = value;
        }
        mTheta += Math.PI / 192;
        if (mTheta > (2 * Math.PI))
            mTheta -= (2 * Math.PI);
    }

    public void paint(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        setAntialiasing(g2);
        setClip(g2);
        setTransform(g2);
        Shape shape = createShape();
        setPaint(g2);

        g2.fill(shape);

        if (mOutline) {
            setStroke(g2);
            g2.setPaint(Color.blue);
            g2.draw(shape);
        }
        drawAxes(g2);
    }

    protected void setAntialiasing(Graphics2D g2) {
        if (mAntialiasing == false)
            return;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    }

    protected void setClip(Graphics2D g2) {
        if (mClip == false)
            return;
        if (mClipShape == null) {
            Dimension d = getSize();
            FontRenderContext frc = g2.getFontRenderContext();
            Font font = new Font("Serif", Font.PLAIN, 144);
            String s = "Java Source and Support!";
            GlyphVector gv = font.createGlyphVector(frc, s);
            Rectangle2D bounds = font.getStringBounds(s, frc);
            mClipShape = gv.getOutline((d.width - (float) bounds.getWidth()) / 2,
                    (d.height + (float) bounds.getHeight()) / 2);
        }
        g2.clip(mClipShape);
    }

    protected void setTransform(Graphics2D g2) {
        if (mTransform == false)
            return;
        Dimension d = getSize();
        g2.rotate(mTheta, d.width / 2, d.height / 2);
    }

    protected Shape createShape() {
        GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD, mPoints.length);
        path.moveTo(mPoints[0], mPoints[1]);
        for (int i = 2; i < mN; i += 6)
            path.curveTo(mPoints[i], mPoints[i + 1], mPoints[i + 2], mPoints[i + 3], mPoints[i + 4],
                    mPoints[i + 5]);
        path.closePath();
        return path;
    }

    protected void setPaint(Graphics2D g2) {
        if (mGradient) {
            GradientPaint gp = new GradientPaint(0, 0, Color.yellow, 50, 25, Color.red, true);
            g2.setPaint(gp);
        } else
            g2.setPaint(Color.orange);
    }

    protected void setStroke(Graphics2D g2) {
        if (mDotted == false)
            return;

        Stroke stroke = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 10, new float[] { 4, 4 },
                0);
        g2.setStroke(stroke);
    }

    protected void drawAxes(Graphics2D g2) {
        if (mAxes == false)
            return;
        g2.setPaint(getForeground());
        g2.setStroke(new BasicStroke());
        Dimension d = getSize();
        int side = 20;
        int arrow = 4;
        int w = d.width / 2, h = d.height / 2;
        g2.drawLine(w - side, h, w + side, h);
        g2.drawLine(w + side - arrow, h - arrow, w + side, h);
        g2.drawLine(w, h - side, w, h + side);
        g2.drawLine(w + arrow, h + side - arrow, w, h + side);
    }

    public void run() {
        while (trucking) {
            render();
            timeStep();
            calculateFrameRate();
        }
    }

    protected void render() {
        Graphics g = getGraphics();
        if (g != null) {
            Dimension d = getSize();
            if (checkImage(d)) {
                Graphics imageGraphics = image.getGraphics();

                imageGraphics.setColor(getBackground());
                imageGraphics.fillRect(0, 0, d.width, d.height);
                imageGraphics.setColor(getForeground());

                paint(imageGraphics);

                g.drawImage(image, 0, 0, null);

                imageGraphics.dispose();
            }
            g.dispose();
        }
    }

    // Offscreen image.
    protected boolean checkImage(Dimension d) {
        if (d.width == 0 || d.height == 0)
            return false;
        if (image == null || image.getWidth(null) != d.width || image.getHeight(null) != d.height) {
            image = createImage(d.width, d.height);
        }
        return true;
    }

    protected void calculateFrameRate() {
        // Measure the frame rate
        long now = System.currentTimeMillis();
        int numberOfFrames = previousTimes.length;
        double newRate;
        // Use the more stable method if a history is available.
        if (previousFilled)
            newRate = (double) numberOfFrames / (double) (now - previousTimes[previousIndex]) * 1000.0;
        else
            newRate = 1000.0 / (double) (now - previousTimes[numberOfFrames - 1]);
        firePropertyChange("frameRate", frameRate, newRate);
        frameRate = newRate;
        // Update the history.
        previousTimes[previousIndex] = now;
        previousIndex++;
        if (previousIndex >= numberOfFrames) {
            previousIndex = 0;
            previousFilled = true;
        }
    }

    public double getFrameRate() {
        return frameRate;
    }

    // Property change support.
    private transient AnimationFrame mRateListener;

    public void setRateListener(AnimationFrame af) {
        mRateListener = af;
    }

    public void firePropertyChange(String name, double oldValue, double newValue) {
        mRateListener.rateChanged(newValue);
    }

}

class AnimationFrame extends JFrame {
    private Label mStatusLabel;

    private NumberFormat mFormat;

    public AnimationFrame(Bouncer ac) {
        super();
        setLayout(new BorderLayout());
        add(ac, BorderLayout.CENTER);
        add(mStatusLabel = new Label(), BorderLayout.SOUTH);
        // Create a number formatter.
        mFormat = NumberFormat.getInstance();
        mFormat.setMaximumFractionDigits(1);
        // Listen for the frame rate changes.
        ac.setRateListener(this);
        // Kick off the animation.
        Thread t = new Thread(ac);
        t.start();
    }

    public void rateChanged(double frameRate) {
        mStatusLabel.setText(mFormat.format(frameRate) + " fps");
    }
}