// SpinIllusion.java // by Brett Allen // version 1.0: June 25, 2001 // // based on Sun's "BezierAnim" example import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import java.awt.image.BufferedImage; import javax.swing.*; import javax.swing.event.*; public class SpinIllusion extends JApplet { Demo demo; public void init() { getContentPane().add(demo = new Demo()); getContentPane().add("North", new DemoControls(demo)); } public void start() { demo.start(); } public void stop() { demo.stop(); } /** * The Demo class performs the animation and the painting. */ public class Demo extends JPanel implements Runnable { private Thread thread; private BufferedImage bimg; protected double skew = 0.3; protected int numAround = 30; protected int numHigh = 2; protected double pWidth = 10; protected double pHeight = 10; protected double pScale = 1.0; protected double relHeight = 0.1; protected int pFrame = 0; protected int pSpeed = 0; protected boolean shading = true; public Demo() { setBackground(Color.lightGray); } public void reset(int w, int h) { } public void step(int w, int h) { if (pSpeed == 0) { pScale = 1.0; return; } int max = 40 - pSpeed * 2; pFrame = (pFrame + 1) % max; if (pFrame > max/2) { pScale = 0.5 + (pFrame - max/2.0) / max; } else { pScale = 0.5 + (max/2.0 - pFrame) / max; } } protected void drawSeg(boolean parity, Graphics2D g2) { AffineTransform oldAF = g2.getTransform(); if (parity) g2.shear(skew, 0); else g2.shear(-skew, 0); g2.setPaint(Color.white); if (shading) g2.setPaint(parity?Color.white:Color.gray); g2.draw(new Line2D.Double(-pWidth/2.0, -pHeight/2.0, -pWidth/2.0, pHeight/2.0)); if (shading) g2.setPaint(parity?Color.white:Color.white); g2.draw(new Line2D.Double(-pWidth/2.0, pHeight/2.0, pWidth/2.0, pHeight/2.0)); if (shading) g2.setPaint(parity?Color.gray:Color.white); g2.draw(new Line2D.Double(pWidth/2.0, pHeight/2.0, pWidth/2.0, -pHeight/2.0)); if (shading) g2.setPaint(parity?Color.gray:Color.gray); g2.draw(new Line2D.Double(pWidth/2.0, -pHeight/2.0, -pWidth/2.0, -pHeight/2.0)); g2.setTransform(oldAF); } // sets the points of the path and draws and fills the path public void drawDemo(int w, int h, Graphics2D g2) { int i, j; AffineTransform oldAF = g2.getTransform(); Dimension d = getSize(); g2.translate(d.width / 2.0, d.height / 2.0); g2.scale(pScale, pScale); AffineTransform curAF = g2.getTransform(); double rad = Math.min(d.width / 2.0, d.height / 2.0); g2.setPaint(Color.black); g2.fillOval(-2, -2, 4, 4); pHeight = rad*relHeight; pWidth = 2.0 * Math.PI * (rad - pHeight) / (2.0 * numAround); for (j=0; j < numHigh; j++) { double curRad = rad - (j*2 + 1) * pHeight; int newNumAround = (int)(1.1 * numAround * curRad / rad); for (i=0; i < newNumAround; i++) { g2.setTransform(curAF); g2.rotate(i * 2.0 * Math.PI / newNumAround, 0, 0); g2.translate(0, curRad); drawSeg(j%2 == 0, g2); } } g2.setTransform(oldAF); } public Graphics2D createGraphics2D(int w, int h) { Graphics2D g2 = null; if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h) { bimg = (BufferedImage) createImage(w, h); reset(w, h); } g2 = bimg.createGraphics(); g2.setBackground(getBackground()); g2.clearRect(0, 0, w, h); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); return g2; } public void paint(Graphics g) { Dimension d = getSize(); step(d.width, d.height); Graphics2D g2 = createGraphics2D(d.width, d.height); drawDemo(d.width, d.height, g2); g2.dispose(); if (bimg != null) { g.drawImage(bimg, 0, 0, this); } } public void start() { thread = new Thread(this); thread.setPriority(Thread.MIN_PRIORITY); thread.start(); } public synchronized void stop() { thread = null; } public void run() { Thread me = Thread.currentThread(); while (thread == me) { repaint(); try { thread.sleep(5); } catch (Exception e) { break; } } thread = null; } } // End Demo class /** * The DemoControls class controls fills and strokes. */ static class DemoControls extends JPanel implements ChangeListener { Demo demo; JSlider skewSlider; JSlider animSlider; JSlider heightSlider, rowSlider; JCheckBox shadingCB; Thread thread; public DemoControls(Demo demo) { JPanel tempPanel; this.demo = demo; setLayout(new GridLayout(5, 1)); tempPanel = new JPanel(); tempPanel.add(new JLabel("Skew:")); skewSlider = new JSlider(JSlider.HORIZONTAL, -10, 10, 5); skewSlider.addChangeListener(this); skewSlider.setMajorTickSpacing(5); skewSlider.setMinorTickSpacing(1); skewSlider.setPaintTicks(true); skewSlider.setPaintLabels(true); skewSlider.setSnapToTicks(true); tempPanel.add(skewSlider); add(tempPanel); tempPanel = new JPanel(); tempPanel.add(new JLabel("Row height:")); heightSlider = new JSlider(JSlider.HORIZONTAL, 1, 20, 10); heightSlider.addChangeListener(this); heightSlider.setMajorTickSpacing(5); heightSlider.setMinorTickSpacing(1); heightSlider.setPaintTicks(true); heightSlider.setPaintLabels(true); heightSlider.setSnapToTicks(true); tempPanel.add(heightSlider); add(tempPanel); tempPanel = new JPanel(); tempPanel.add(new JLabel("# of rows:")); rowSlider = new JSlider(JSlider.HORIZONTAL, 1, 6, 1); rowSlider.addChangeListener(this); rowSlider.setMajorTickSpacing(1); rowSlider.setMinorTickSpacing(1); rowSlider.setPaintTicks(true); rowSlider.setPaintLabels(true); rowSlider.setSnapToTicks(true); tempPanel.add(rowSlider); add(tempPanel); tempPanel = new JPanel(); tempPanel.add(new JLabel("Animation:")); animSlider = new JSlider(JSlider.HORIZONTAL, 0, 10, 0); animSlider.addChangeListener(this); animSlider.setMajorTickSpacing(5); animSlider.setMinorTickSpacing(1); animSlider.setPaintTicks(true); animSlider.setPaintLabels(true); animSlider.setSnapToTicks(true); tempPanel.add(animSlider); add(tempPanel); tempPanel = new JPanel(); shadingCB = new JCheckBox("Shading"); shadingCB.addChangeListener(this); shadingCB.setSelected(true); tempPanel.add(shadingCB); add(tempPanel); } public void stateChanged(ChangeEvent e) { if (e.getSource() == skewSlider) { demo.skew = skewSlider.getValue() / 10.0; } else if (e.getSource() == heightSlider) { demo.relHeight = heightSlider.getValue() / 100.0; } else if (e.getSource() == animSlider) { demo.pSpeed = animSlider.getValue(); } else if (e.getSource() == rowSlider) { demo.numHigh = rowSlider.getValue(); } else if (e.getSource() == shadingCB) { demo.shading = shadingCB.isSelected(); } } } // End DemoControls class public static void main(String argv[]) { final SpinIllusion demo = new SpinIllusion(); demo.init(); Frame f = new Frame("Spin Illusion"); f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) {System.exit(0);} public void windowDeiconified(WindowEvent e) { demo.start(); } public void windowIconified(WindowEvent e) { demo.stop(); } }); f.add(demo); f.pack(); f.setSize(new Dimension(500,600)); f.show(); demo.start(); } } // End SpinIllusion class