package trident.swing;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.io.InputStream;
import java.util.*;
import java.util.List;
import javax.swing.*;
import org.pushingpixels.trident.Timeline;
import org.pushingpixels.trident.TimelineScenario;
import org.pushingpixels.trident.Timeline.RepeatBehavior;
import org.pushingpixels.trident.callback.TimelineScenarioCallback;
import org.pushingpixels.trident.swing.SwingRepaintTimeline;
public class MatrixRain {
private static Font font;
public static class Letter {
private int x;
private int y;
private Color color;
private float opacity;
private char c;
public Letter(int x, int y, char c) {
this.x = x;
this.y = y;
this.c = c;
this.color = Color.white;
this.opacity = 0.0f;
}
public void setOpacity(float opacity) {
this.opacity = opacity;
}
public void setColor(Color color) {
this.color = color;
}
public void paint(Graphics g) {
if (this.opacity == 0.0f)
return;
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2d.setFont(font);
g2d.setColor(this.color);
g2d.setComposite(AlphaComposite.SrcOver.derive(this.opacity));
g2d.drawString("" + this.c, this.x, this.y);
g2d.dispose();
}
}
public static class Drop {
private List<Letter> letters;
public Drop() {
this.letters = new ArrayList<Letter>();
}
public TimelineScenario getScenario(int x) {
TimelineScenario result = new TimelineScenario.Parallel();
Random randomizer = new Random();
// how many letters will there be?
int totalLetterCount = 5 + randomizer.nextInt(20);
int initialDelay = randomizer.nextInt(1000);
int duration = 1000 + randomizer.nextInt(100);
for (int i = 0; i < totalLetterCount; i++) {
int y = font.getSize() * i;
// choose random katakana letter
// int letterIndex = (int) (0x30A0 + Math.random()
// * (0x30FF - 0x30A0));
int start = 33;
int delta = 95;
char c = (char) (start + Math.random() * delta);
Letter l = new Letter(x, y, c);
this.letters.add(l);
Timeline t = new Timeline(l);
t.addPropertyToInterpolate("opacity", 1.0f, 0.0f);
t.addPropertyToInterpolate("color", Color.white, Color.green);
t.setDuration(duration);
t.setInitialDelay(initialDelay + i * 120);
result.addScenarioActor(t);
}
return result;
}
public void paint(Graphics g) {
for (Letter l : this.letters)
l.paint(g);
}
}
private static class MatrixPanel extends JPanel {
private List<Drop> drops;
public MatrixPanel() {
try {
InputStream is = MatrixRain.class.getClassLoader()
.getResourceAsStream("test/swing/katakana.ttf");
Font kf = Font.createFont(Font.TRUETYPE_FONT, is);
int fontSize = 14;
font = kf.deriveFont(Font.BOLD, fontSize);
} catch (Exception exc) {
exc.printStackTrace();
}
Timeline repaint = new SwingRepaintTimeline(this);
repaint.playLoop(RepeatBehavior.LOOP);
this.drops = new ArrayList<Drop>();
this.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
while (drops.size() < 40)
addDrop();
}
});
}
private synchronized void addDrop() {
final Drop drop = new Drop();
TimelineScenario scenario = drop.getScenario(new Random()
.nextInt(getWidth()));
scenario.addCallback(new TimelineScenarioCallback() {
@Override
public void onTimelineScenarioDone() {
synchronized (MatrixPanel.this) {
drops.remove(drop);
addDrop();
}
}
});
this.drops.add(drop);
scenario.play();
}
@Override
protected void paintComponent(Graphics g) {
g.setColor(Color.black);
g.fillRect(0, 0, getWidth(), getHeight());
synchronized (this) {
for (Drop drop : this.drops)
drop.paint(g);
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame fr = new JFrame("Matrix rain");
fr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MatrixPanel panel = new MatrixPanel();
panel.setPreferredSize(new Dimension(400, 300));
fr.add(panel);
fr.pack();
fr.setLocationRelativeTo(null);
fr.setVisible(true);
}
});
}
}