import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.*;
import javax.swing.*;
import java.util.Random;
import java.util.function.Consumer;
public class TilesPanel extends JComponent {
private Color[] colors;
private int tilesPerRow;
private double balance;
public TilesPanel(Color[] colors, int tilesPerRow) {
this.colors = colors;
this.tilesPerRow = tilesPerRow;
}
public int getTileCount() {
return colors.length;
}
public Color[] getColors() { return colors; }
public Color getTileColor(int ix) {
return colors[ix];
}
public void setColors(Color[] colors) {
this.colors = colors;
repaint();
}
public int getTileIndex(Point p) {
Point tile = viewToTile(p);
return tilesPerRow * tile.y + tile.x;
}
public Point viewToTile(Point p) {
Rectangle2D box = getDrawingBox();
double x = p.getX() - box.getX();
double y = p.getY() - box.getY();
Rectangle2D size = getTileRect();
int xTile = (int)(x / size.getWidth());
int yTile = (int)(y / size.getHeight());
return new Point(xTile, yTile);
}
public Rectangle2D getDrawingBox() {
Insets insets = getInsets();
int x = insets.left;
int y = insets.top;
double w = getWidth() - insets.left - insets.right;
double h = getHeight() - insets.top - insets.bottom;
return new Rectangle2D.Double(x, y, w, h);
}
public Rectangle2D getTileRect() {
Rectangle2D box = getDrawingBox();
double tileWidth = box.getWidth() / tilesPerRow;
double tileHeight = box.getHeight() / getRows();
return new Rectangle2D.Double(0, 0, tileWidth, tileHeight);
}
private int getRows() {
return (colors.length + tilesPerRow - 1) / tilesPerRow;
}
@Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Rectangle2D tile = getTileRect();
double tileWidth = tile.getWidth();
double tileHeight = tile.getHeight();
g2.translate(getInsets().left, getInsets().top);
for (int i = 0; i < colors.length; i++) {
drawTile(g2, tile, i);
g2.translate(tileWidth, 0);
if ((i+1) % tilesPerRow == 0) {
g2.translate(-tileWidth*tilesPerRow, tileHeight);
}
}
}
private void drawTile(Graphics2D g, Rectangle2D tile, int ix) {
g.setColor(colors[ix]);
g.fill(tile);
g.setColor(Color.BLACK);
g.draw(tile);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
Random rand = new Random(1L);
Color[] colors = randomColors(400, rand);
Animation animation = new Animation();
TilesPanel panel = new TilesPanel(colors, 20);
panel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
int ix = panel.getTileIndex(e.getPoint());
Color[] destColors = randomColors(colors.length, rand);
destColors[ix] = panel.getTileColor(ix);
ColorTransition colorTransition = new ColorTransition(
panel.getColors(), destColors);
animation.start(500, v -> panel.setColors(colorTransition.perform(v)));
}
});
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(panel);
frame.setSize(600, 400);
frame.setVisible(true);
});
}
public static Color[] randomColors(int num, Random rand) {
Color[] colors = new Color[num];
for (int i = 0; i < colors.length; i++) {
colors[i] = new Color((int)(rand.nextInt()));
}
return colors;
}
private static class ColorTransition {
private Color[] source;
private Color[] dest;
public ColorTransition(Color[] source, Color[] dest) {
this.source = source;
this.dest = dest;
}
public Color[] perform(double value) {
Color[] colors = new Color[source.length];
for (int i = 0; i < colors.length; i++) {
colors[i] = transformedColor(i, value);
}
return colors;
}
private Color transformedColor(int ix, double value) {
int red = dest[ix].getRed() - source[ix].getRed();
int green = dest[ix].getGreen() - source[ix].getGreen();
int blue = dest[ix].getBlue() - source[ix].getBlue();
double factor = value * value;
return new Color(
(int)(source[ix].getRed() + red * factor),
(int)(source[ix].getGreen() + green * factor),
(int)(source[ix].getBlue() + blue * factor));
}
}
private static class Animation {
private Timer timer;
public void start(long duration, Consumer<Double> consumer) {
if (timer == null) {
long start = System.currentTimeMillis();
timer = new Timer(10, e -> processTimerEvent(start, duration, consumer));
timer.setRepeats(true);
timer.start();
}
}
public void stop() {
if (timer != null) {
timer.stop();
timer = null;
}
}
private void processTimerEvent(long start, long duration, Consumer<Double> consumer) {
double value = (System.currentTimeMillis() - start) / (double) duration;
if (value >= 1.0) {
consumer.accept(1.0);
stop();
} else {
consumer.accept(value);
}
}
}
}