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}