001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.animation;
003
004import java.awt.Color;
005import java.awt.Graphics;
006import java.awt.Point;
007import java.awt.Polygon;
008import java.util.Random;
009
010/**
011 * A star displayed when {@link ChristmasExtension} is active. Copied from Icedtea-Web.
012 * @author Jiri Vanek (Red Hat)
013 * @see <a href="http://icedtea.classpath.org/hg/icedtea-web/rev/87d3081ab573">Initial commit</a>
014 * @since 14581
015 */
016class Star {
017    private static final Random seed = new Random();
018
019    static final int averageStarWidth = 10;    // stars will be 5-15
020    static final int averageFallSpeed = 4;     // 2-6
021    static final int averageRotationSpeed = 2; // 1-3
022
023    private static final Color WATER_LIVE_COLOR = new Color(80, 131, 160);
024
025    private final int w;
026    private final int h;
027
028    private int radiusX;
029    private int radiusY;
030    private int maxRadiusX;
031    private int maxRadiusY;
032    private final Point center = new Point();
033    private final int fallSpeed;
034    private final boolean orientation;
035    private final int[] originalColor = new int[3];
036    private final int[] color = new int[originalColor.length];
037    private int direction;
038    private final boolean haveEight;
039
040    Star(int w, int h) {
041        this.w = w;
042        this.h = h;
043        createRadiuses();
044        haveEight = seed.nextBoolean();
045        center.x = seed.nextInt(w + 1);
046        center.y = seed.nextInt(h + 1);
047        fallSpeed = averageFallSpeed / 2 + seed.nextInt(averageFallSpeed / 2);
048        orientation = seed.nextBoolean();
049        direction = -(averageRotationSpeed / 2 + seed.nextInt(averageRotationSpeed / 2));
050        if (seed.nextInt(4) == 0) {
051            originalColor[0] = Color.yellow.getRed();
052            originalColor[1] = Color.yellow.getGreen();
053            originalColor[2] = Color.yellow.getBlue();
054        } else {
055            originalColor[0] = WATER_LIVE_COLOR.getRed();
056            originalColor[1] = WATER_LIVE_COLOR.getGreen();
057            originalColor[2] = WATER_LIVE_COLOR.getBlue();
058        }
059    }
060
061    void paint(Graphics g) {
062        Color c = g.getColor();
063        g.setColor(new Color(color[0], color[1], color[2]));
064        Polygon p = createPolygon();
065        if (haveEight) {
066            int min1 = Math.min(radiusX, radiusY);
067            int min2 = min1 / 2;
068            g.fillRect(center.x - min2, center.y - min2, min1, min1);
069        }
070        g.fillPolygon(p);
071        g.setColor(c);
072    }
073
074    void animate() {
075        center.y += fallSpeed;
076        if (orientation) {
077            radiusX += direction;
078            if (radiusX <= -direction) {
079                direction = -direction;
080                radiusX = direction;
081            }
082            if (radiusX >= maxRadiusX) {
083                direction = -direction;
084                radiusX = maxRadiusX;
085            }
086            interpolateColors(radiusX, maxRadiusX);
087        } else {
088            radiusY += direction;
089            if (radiusY <= -direction) {
090                direction = -direction;
091                radiusY = direction;
092            }
093            if (radiusY >= maxRadiusY) {
094                direction = -direction;
095                radiusY = maxRadiusY;
096            }
097            interpolateColors(radiusY, maxRadiusY);
098        }
099        if (center.y > h + radiusX * 2 || center.y > h + radiusY * 2) {
100            createRadiuses();
101            center.x = seed.nextInt(w + 1);
102            center.y = -radiusY * 2;
103        }
104    }
105
106    private static int createRadius() {
107        return averageStarWidth / 2 + seed.nextInt(averageStarWidth);
108    }
109
110    private Polygon createPolygon() {
111        int min = Math.min(radiusX, radiusY) / 3;
112        Polygon p = new Polygon();
113        p.addPoint(center.x - radiusX, center.y);
114        p.addPoint(center.x - min, center.y - min);
115        p.addPoint(center.x, center.y - radiusY);
116        p.addPoint(center.x + min, center.y - min);
117        p.addPoint(center.x + radiusX, center.y);
118        p.addPoint(center.x + min, center.y + min);
119        p.addPoint(center.x, center.y + radiusY);
120        p.addPoint(center.x - min, center.y + min);
121        return p;
122    }
123
124    private void interpolateColors(int is, int max) {
125        for (int i = 0; i < originalColor.length; i++) {
126            int fadeMin;
127            if (center.y < 0) {
128                fadeMin = 0;
129            } else if (center.y > h) {
130                fadeMin = 255;
131            } else {
132                fadeMin = (int) interpol(h, center.y, 255, 0); // from white to black
133            }
134            int fadeMax;
135            if (center.y < 0) {
136                fadeMax = 0;
137            } else if (center.y > h) {
138                fadeMax = originalColor[i];
139            } else {
140                fadeMax = (int) interpol(h, center.y, originalColor[i], 0); // from color to black
141            }
142            color[i] = (int) interpol(max, is, fadeMin, fadeMax);
143        }
144    }
145
146    private void createRadiuses() {
147        radiusX = createRadius();
148        radiusY = radiusX;
149        switch (seed.nextInt(3)) {
150            case 0:
151                radiusX = radiusX + (2 * radiusX) / 3;
152                break;
153            case 1:
154                radiusY = radiusY + (2 * radiusY) / 3;
155                break;
156            default:
157                break;
158        }
159        maxRadiusX = radiusX;
160        maxRadiusY = radiusY;
161    }
162
163    /**
164     * Interpolation is root ratio is r= (currentSize / origSize)
165     * then value to-from is interpolated from to to from according to ratio
166     *
167     * @param origSize original size
168     * @param currentSize current size
169     * @param from starting value
170     * @param to ending value
171     * @return interpolated value
172     */
173    static double interpol(double origSize, double currentSize, double from, double to) {
174        return (currentSize / origSize) * (to - from) + from;
175    }
176}