Hypnosis.java Source code

Java tutorial

Introduction

Here is the source code for Hypnosis.java

Source

/*
 * Copyright (c) 2000 David Flanagan.  All rights reserved.
 * This code is from the book Java Examples in a Nutshell, 2nd Edition.
 * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
 * You may study, use, and modify it for any non-commercial purpose.
 * You may distribute it non-commercially as long as you retain this notice.
 * For a commercial use license, or to purchase the book (recommended),
 * visit http://www.davidflanagan.com/javaexamples2.
 */

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.Timer;

/**
 * A Swing component that smoothly animates a spiral in a hypnotic way.
 */
public class Hypnosis extends JComponent implements ActionListener {
    double x, y; // The center of the spiral

    double r1, r2; // The inner and outer radii of the spiral

    double a1, a2; // The start and end angles of the spiral

    double deltaA; // How much the angle changes each frame

    double deltaX, deltaY; // The trajectory of the center

    float linewidth; // How wide the lines are

    Timer timer; // The object that triggers the animation

    BufferedImage buffer; // The image we use for double-buffering

    Graphics2D osg; // Graphics2D object for drawing into the buffer

    public Hypnosis(double x, double y, double r1, double r2, double a1, double a2, float linewidth, int delay,
            double deltaA, double deltaX, double deltaY) {
        this.x = x;
        this.y = y;
        this.r1 = r1;
        this.r2 = r2;
        this.a1 = a1;
        this.a2 = a2;
        this.linewidth = linewidth;
        this.deltaA = deltaA;
        this.deltaX = deltaX;
        this.deltaY = deltaY;

        // Set up a timer to call actionPerformed() every delay milliseconds
        timer = new Timer(delay, this);

        // Create a buffer for double-buffering
        buffer = new BufferedImage((int) (2 * r2 + linewidth), (int) (2 * r2 + linewidth),
                BufferedImage.TYPE_INT_RGB);

        // Create a Graphics object for the buffer, and set the linewidth
        // and request antialiasing when drawing with it
        osg = buffer.createGraphics();
        osg.setStroke(new BasicStroke(linewidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
        osg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    }

    // Start and stop the animation by starting and stopping the timer
    public void start() {
        timer.start();
    }

    public void stop() {
        timer.stop();
    }

    /**
     * Swing calls this method to ask the component to redraw itself. This
     * method uses double-buffering to make the animation smoother. Swing does
     * double-buffering automatically, so this may not actually make much
     * difference, but it is important to understand the technique.
     */
    public void paintComponent(Graphics g) {
        // Clear the background of the off-screen image
        osg.setColor(getBackground());
        osg.fillRect(0, 0, buffer.getWidth(), buffer.getHeight());

        // Now draw a black spiral into the off-screen image
        osg.setColor(Color.black);
        osg.draw(new Spiral(r2 + linewidth / 2, r2 + linewidth / 2, r1, a1, r2, a2));

        // Now copy that off-screen image onto the screen
        g.drawImage(buffer, (int) (x - r2), (int) (y - r2), this);
    }

    /**
     * This method implements the ActionListener interface. Our Timer object
     * calls this method periodically. It updates the position and angles of the
     * spiral and requests a redraw. Instead of redrawing the entire component,
     * however, this method requests a redraw only for the area that has
     * changed.
     */
    public void actionPerformed(ActionEvent e) {
        // Ask to have the old bounding box of the spiral redrawn.
        // Nothing else has anything drawn in it, so it doesn't need a redraw
        repaint((int) (x - r2 - linewidth), (int) (y - r2 - linewidth), (int) (2 * (r2 + linewidth)),
                (int) (2 * (r2 + linewidth)));

        // Now animate: update the position and angles of the spiral

        // Bounce if we've hit an edge
        Rectangle bounds = getBounds();
        if ((x - r2 + deltaX < 0) || (x + r2 + deltaX > bounds.width))
            deltaX = -deltaX;
        if ((y - r2 + deltaY < 0) || (y + r2 + deltaY > bounds.height))
            deltaY = -deltaY;

        // Move the center of the spiral
        x += deltaX;
        y += deltaY;

        // Increment the start and end angles;
        a1 += deltaA;
        a2 += deltaA;
        if (a1 > 2 * Math.PI) { // Don't let them get too big
            a1 -= 2 * Math.PI;
            a2 -= 2 * Math.PI;
        }

        // Now ask to have the new bounding box of the spiral redrawn. This
        // rectangle will be intersected with the redraw rectangle requested
        // above, and only the combined region will be redrawn
        repaint((int) (x - r2 - linewidth), (int) (y - r2 - linewidth), (int) (2 * (r2 + linewidth)),
                (int) (2 * (r2 + linewidth)));
    }

    /** Tell Swing not to double-buffer for us, since we do our own */
    public boolean isDoubleBuffered() {
        return false;
    }

    /** This is a main() method for testing the component */
    public static void main(String[] args) {
        JFrame f = new JFrame("Hypnosis");
        Hypnosis h = new Hypnosis(200, 200, 10, 100, 0, 11 * Math.PI, 7, 100, 2 * Math.PI / 30, 3, 5);
        f.getContentPane().add(h, BorderLayout.CENTER);
        f.setSize(400, 400);
        f.show();
        h.start();
    }
}