import java.awt.*; import java.awt.geom.*; import java.awt.event.*; import java.awt.image.*; import java.awt.font.*; import java.util.*; class Confetti extends Frame { private static final String[] VEGETABLES = { "celery", "rutabaga", "lettuce", "eggplant", "squash", "cucumber", "broccoli", "spinach", "leeks", "capers", "onions", "peas", "turnips" }; private static final int DEFAULT_WINDOW_WIDTH = 600, DEFAULT_WINDOW_HEIGHT = 200; private static final int NUM_ADDITIONAL_PARTICLES = 200; private static final double DT = 0.05; // seconds AffineTransform _transform; final List12 _particles; final Random _random; final Label _numParticlesLabel; Shape _textShape; Shape createShapeFromText (String text) { final BufferedImage image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); final Graphics2D g2 = image.createGraphics(); Shape shape = null; final Font font = new Font("SansSerif", Font.BOLD, 72); try { final GlyphVector vector = font.createGlyphVector(g2.getFontRenderContext(), text); shape = vector.getOutline(); final Rectangle bounds = shape.getBounds(); final AffineTransform textTransform = new AffineTransform(); textTransform.scale(getWidth() / bounds.width * 0.75, getHeight() / bounds.height * 0.5); textTransform.scale(1.0 / getWidth(), 1.0 / getHeight()); textTransform.translate(- bounds.width / 2, + bounds.height / 2); shape = textTransform.createTransformedShape(shape); } finally { g2.dispose(); } return shape; } void resetGeometry () { _transform = new AffineTransform(); _transform.scale(getWidth(), getHeight()); _transform.translate(+0.5, +0.5); } Confetti (String username) { _random = new Random(); _particles = new DoublyLinkedList12(); setSize(new Dimension(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT)); setLayout(new FlowLayout()); resetGeometry(); _textShape = createShapeFromText(VEGETABLES[(int) Math.abs(username.hashCode()) % VEGETABLES.length]); final Button addButton = new Button("Add more confetti"); addButton.addActionListener(new ActionListener() { public void actionPerformed (ActionEvent e) { addMoreParticles(); } }); add(addButton); final Button removeButton = new Button("Remove loose confetti"); removeButton.addActionListener(new ActionListener() { public void actionPerformed (ActionEvent e) { removeSomeParticles(); } }); add(removeButton); _numParticlesLabel = new Label(); _numParticlesLabel.setPreferredSize(new Dimension(100, 30)); add(_numParticlesLabel); addComponentListener(new ComponentAdapter() { public void componentResized (ComponentEvent e) { resetGeometry(); } }); // Start simulation java.util.Timer timer = new java.util.Timer(); timer.schedule(new TimerTask() { public void run () { update(); } }, 0, (long) (DT * 1000)); } synchronized void removeSomeParticles () { final Iterator iterator = _particles.iterator(); int count = 0; while (iterator.hasNext() && count < NUM_ADDITIONAL_PARTICLES) { final Particle particle = (Particle) iterator.next(); if (! particle._caught) { iterator.remove(); count++; } } } synchronized void addMoreParticles () { for (int i = 0; i < NUM_ADDITIONAL_PARTICLES; i++) { _particles.add(new Particle()); } } synchronized void update () { for (Object particle : _particles) { ((Particle) particle).update(); } _numParticlesLabel.setText(_particles.size() + " particles"); repaint(); } class Particle { private static final double DECAY = 1 - 1e-1; private static final double PARTICLE_SIZE = 0.01; private static final double MAX_SPEED = 0.1; private double _x, _y, _dx, _dy; private final Ellipse2D.Double _shape; private final Color _color; private boolean _caught; Particle () { // Initialize with random position and velocity _x = _random.nextDouble() - 0.5; _y = _random.nextDouble() - 0.5; _dx = _random.nextDouble() - 0.5; _dy = _random.nextDouble() - 0.5; _shape = new Ellipse2D.Double(); _color = new Color(_random.nextFloat(), _random.nextFloat(), _random.nextFloat(), 1); _caught = false; } void update () { if (_caught) { return; // shortcut } _x += _dx * DT; _y += _dy * DT; _dx *= DECAY; _dy *= DECAY; // Check for boundary conditions -- make particles bounce if (_textShape.contains(_x, _y)) { // Caught in text! if (_random.nextFloat() < 0.2) { _dx = 0; _dy = 0; _caught = true; } } else { if (_x < -0.5 || _x > 0.5) { _dx *= -1; } if (_y < -0.5 || _y > 0.5) { _dy *= -1; } } _shape.setFrame(_x, _y, PARTICLE_SIZE, PARTICLE_SIZE); } void paint (Graphics2D g2) { g2.setColor(_color); g2.fill(_shape); } } synchronized public void paint (Graphics g) { final Graphics2D g2 = (Graphics2D) g; g2.setTransform(_transform); for (Object particle : _particles) { ((Particle) particle).paint(g2); } } public static void main (String[] args) { final Map env = System.getenv(); final String username = env.get("USER"); if (username == null) { System.out.println("I couldn't determine your vegetable name! You need to run on ieng6"); System.exit(1); } final Confetti confetti = new Confetti(username); confetti.setVisible(true); } }