1 /*
2  *
3  * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  *   - Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer.
11  *
12  *   - Redistributions in binary form must reproduce the above copyright
13  *     notice, this list of conditions and the following disclaimer in the
14  *     documentation and/or other materials provided with the distribution.
15  *
16  *   - Neither the name of Oracle nor the names of its
17  *     contributors may be used to endorse or promote products derived
18  *     from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
21  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 package java2d.demos.Mix;
33 
34 
35 import static java.awt.Color.BLUE;
36 import static java.awt.Color.GREEN;
37 import static java.awt.Color.ORANGE;
38 import static java.awt.Color.RED;
39 import static java.awt.Color.WHITE;
40 import static java.awt.Color.YELLOW;
41 import static java.lang.Math.random;
42 import static java.lang.Math.sqrt;
43 import java.awt.Color;
44 import java.awt.Component;
45 import java.awt.Dimension;
46 import java.awt.Graphics2D;
47 import java.awt.event.ActionEvent;
48 import java.awt.event.ActionListener;
49 import java.awt.image.BufferedImage;
50 import java.awt.image.DataBufferByte;
51 import java.awt.image.IndexColorModel;
52 import java.awt.image.Raster;
53 import java.awt.image.WritableRaster;
54 import java2d.AnimatingControlsSurface;
55 import java2d.CustomControls;
56 import javax.swing.AbstractButton;
57 import javax.swing.JComboBox;
58 import javax.swing.JToggleButton;
59 import javax.swing.JToolBar;
60 
61 
62 /**
63  * Animated color bouncing balls with custom controls.
64  */
65 @SuppressWarnings("serial")
66 public class Balls extends AnimatingControlsSurface {
67 
68     private static Color[] colors = { RED, ORANGE, YELLOW, GREEN.darker(), BLUE,
69         new Color(75, 00, 82), new Color(238, 130, 238) };
70     private long now, deltaT, lasttime;
71     private boolean active;
72     protected Ball[] balls = new Ball[colors.length];
73     protected boolean clearToggle;
74     protected JComboBox<String> combo;
75 
Balls()76     public Balls() {
77         setBackground(WHITE);
78         for (int i = 0; i < colors.length; i++) {
79             balls[i] = new Ball(colors[i], 30);
80         }
81         balls[0].isSelected = true;
82         balls[3].isSelected = true;
83         balls[4].isSelected = true;
84         balls[6].isSelected = true;
85         setControls(new Component[] { new DemoControls(this) });
86     }
87 
88     @Override
reset(int w, int h)89     public void reset(int w, int h) {
90         if (w > 400 && h > 100) {
91             combo.setSelectedIndex(5);
92         }
93     }
94 
95     @Override
step(int w, int h)96     public void step(int w, int h) {
97         if (lasttime == 0) {
98             lasttime = System.currentTimeMillis();
99         }
100         now = System.currentTimeMillis();
101         deltaT = now - lasttime;
102         active = false;
103         for (Ball ball : balls) {
104             if (ball == null) {
105                 return;
106             }
107             ball.step(deltaT, w, h);
108             if (ball.Vy > .02 || -ball.Vy > .02 || ball.y + ball.bsize < h) {
109                 active = true;
110             }
111         }
112         if (!active) {
113             for (Ball ball : balls) {
114                 ball.Vx = (float) random() / 4.0f - 0.125f;
115                 ball.Vy = -(float) random() / 4.0f - 0.2f;
116             }
117             clearToggle = true;
118         }
119     }
120 
121     @Override
render(int w, int h, Graphics2D g2)122     public void render(int w, int h, Graphics2D g2) {
123         for (Ball b : balls) {
124             if (b == null || b.imgs[b.index] == null || !b.isSelected) {
125                 continue;
126             }
127             g2.drawImage(b.imgs[b.index], (int) b.x, (int) b.y, this);
128         }
129         lasttime = now;
130     }
131 
main(String[] argv)132     public static void main(String[] argv) {
133         createDemoFrame(new Balls());
134     }
135 
136 
137     protected static final class Ball {
138 
139         public static final int nImgs = 5;
140         public int bsize;
141         public float x, y;
142         public float Vx = 0.1f;
143         public float Vy = 0.05f;
144         public BufferedImage[] imgs;
145         // Pick a random starting image index, but not the last: we're going UP
146         // and that would throw us off the end.
147         public int index = (int) (random() * (nImgs - 1));
148         private static final float inelasticity = .96f;
149         private static final float Ax = 0.0f;
150         private static final float Ay = 0.0002f;
151         private static final int UP = 0;
152         private static final int DOWN = 1;
153         private int indexDirection = UP;
154         private float jitter;
155         private Color color;
156         private boolean isSelected;
157 
Ball(Color color, int bsize)158         public Ball(Color color, int bsize) {
159             this.color = color;
160             makeImages(bsize);
161         }
162 
makeImages(int bsize)163         public void makeImages(int bsize) {
164             this.bsize = bsize * 2;
165             int R = bsize;
166             byte[] data = new byte[R * 2 * R * 2];
167             int maxr = 0;
168             for (int Y = 2 * R; --Y >= 0;) {
169                 int x0 = (int) (sqrt(R * R - (Y - R) * (Y - R)) + 0.5);
170                 int p = Y * (R * 2) + R - x0;
171                 for (int X = -x0; X < x0; X++) {
172                     int xx = X + 15;
173                     int yy = Y - R + 15;
174                     int r = (int) (Math.hypot(xx, yy) + 0.5);
175                     if (r > maxr) {
176                         maxr = r;
177                     }
178                     data[p++] = r <= 0 ? 1 : (byte) r;
179                 }
180             }
181 
182             imgs = new BufferedImage[nImgs];
183 
184             int bg = 255;
185             byte[] red = new byte[256];
186             red[0] = (byte) bg;
187             byte[] green = new byte[256];
188             green[0] = (byte) bg;
189             byte[] blue = new byte[256];
190             blue[0] = (byte) bg;
191 
192             for (int r = 0; r < imgs.length; r++) {
193                 float b = 0.5f + ((r + 1f) / imgs.length / 2f);
194                 for (int i = maxr; i >= 1; --i) {
195                     float d = (float) i / maxr;
196                     red[i] = (byte) blend(blend(color.getRed(), 255, d), bg, b);
197                     green[i] = (byte) blend(blend(color.getGreen(), 255, d), bg,
198                             b);
199                     blue[i] =
200                             (byte) blend(blend(color.getBlue(), 255, d), bg, b);
201                 }
202                 IndexColorModel icm = new IndexColorModel(8, maxr + 1,
203                         red, green, blue, 0);
204                 DataBufferByte dbb = new DataBufferByte(data, data.length);
205                 int[] bandOffsets = { 0 };
206                 WritableRaster wr = Raster.createInterleavedRaster(dbb,
207                         R * 2, R * 2, R * 2, 1, bandOffsets, null);
208                 imgs[r] = new BufferedImage(icm, wr, icm.isAlphaPremultiplied(),
209                         null);
210             }
211         }
212 
blend(int fg, int bg, float fgfactor)213         private int blend(int fg, int bg, float fgfactor) {
214             return (int) (bg + (fg - bg) * fgfactor);
215         }
216 
step(long deltaT, int w, int h)217         public void step(long deltaT, int w, int h) {
218 
219             jitter = (float) random() * .01f - .005f;
220 
221             x += Vx * deltaT + (Ax / 2.0) * deltaT * deltaT;
222             y += Vy * deltaT + (Ay / 2.0) * deltaT * deltaT;
223             if (x <= 0.0f) {
224                 x = 0.0f;
225                 Vx = -Vx * inelasticity + jitter;
226                 //collision_x = true;
227             }
228             if (x + bsize >= w) {
229                 x = w - bsize;
230                 Vx = -Vx * inelasticity + jitter;
231                 //collision_x = true;
232             }
233             if (y <= 0) {
234                 y = 0;
235                 Vy = -Vy * inelasticity + jitter;
236                 //collision_y = true;
237             }
238             if (y + bsize >= h) {
239                 y = h - bsize;
240                 Vx *= inelasticity;
241                 Vy = -Vy * inelasticity + jitter;
242                 //collision_y = true;
243             }
244             Vy = Vy + Ay * deltaT;
245             Vx = Vx + Ax * deltaT;
246 
247             if (indexDirection == UP) {
248                 index++;
249             }
250             if (indexDirection == DOWN) {
251                 --index;
252             }
253             if (index + 1 == nImgs) {
254                 indexDirection = DOWN;
255             }
256             if (index == 0) {
257                 indexDirection = UP;
258             }
259         }
260     }  // End class Ball
261 
262 
263     final class DemoControls extends CustomControls implements ActionListener {
264 
265         Balls demo;
266         JToolBar toolbar;
267 
268         @SuppressWarnings("LeakingThisInConstructor")
DemoControls(Balls demo)269         public DemoControls(Balls demo) {
270             super(demo.name);
271             this.demo = demo;
272             add(toolbar = new JToolBar());
273             toolbar.setFloatable(false);
274             addTool("Clear", true);
275             addTool("R", demo.balls[0].isSelected);
276             addTool("O", demo.balls[1].isSelected);
277             addTool("Y", demo.balls[2].isSelected);
278             addTool("G", demo.balls[3].isSelected);
279             addTool("B", demo.balls[4].isSelected);
280             addTool("I", demo.balls[5].isSelected);
281             addTool("V", demo.balls[6].isSelected);
282             add(combo = new JComboBox<>());
283             combo.addItem("10");
284             combo.addItem("20");
285             combo.addItem("30");
286             combo.addItem("40");
287             combo.addItem("50");
288             combo.addItem("60");
289             combo.addItem("70");
290             combo.addItem("80");
291             combo.setSelectedIndex(2);
292             combo.addActionListener(this);
293         }
294 
addTool(String str, boolean state)295         public void addTool(String str, boolean state) {
296             JToggleButton b =
297                     (JToggleButton) toolbar.add(new JToggleButton(str));
298             b.setFocusPainted(false);
299             b.setSelected(state);
300             b.addActionListener(this);
301             int width = b.getPreferredSize().width;
302             Dimension prefSize = new Dimension(width, 21);
303             b.setPreferredSize(prefSize);
304             b.setMaximumSize(prefSize);
305             b.setMinimumSize(prefSize);
306         }
307 
308         @Override
actionPerformed(ActionEvent e)309         public void actionPerformed(ActionEvent e) {
310             if (e.getSource() instanceof JComboBox) {
311                 int size = Integer.parseInt((String) combo.getSelectedItem());
312                 for (Ball ball : demo.balls) {
313                     ball.makeImages(size);
314                 }
315                 return;
316             }
317             JToggleButton b = (JToggleButton) e.getSource();
318             if (b.getText().equals("Clear")) {
319                 demo.clearSurface = b.isSelected();
320             } else {
321                 int index = toolbar.getComponentIndex(b) - 1;
322                 demo.balls[index].isSelected = b.isSelected();
323             }
324         }
325 
326         @Override
getPreferredSize()327         public Dimension getPreferredSize() {
328             return new Dimension(200, 40);
329         }
330 
331         @Override
332         @SuppressWarnings("SleepWhileHoldingLock")
run()333         public void run() {
334             try {
335                 Thread.sleep(999);
336             } catch (Exception e) {
337                 return;
338             }
339             Thread me = Thread.currentThread();
340             ((AbstractButton) toolbar.getComponentAtIndex(2)).doClick();
341             while (thread == me) {
342                 try {
343                     Thread.sleep(222);
344                 } catch (InterruptedException e) {
345                     return;
346                 }
347                 if (demo.clearToggle) {
348                     if (demo.clearSurface) {
349                         combo.setSelectedIndex((int) (random() * 5));
350                     }
351                     ((AbstractButton) toolbar.getComponentAtIndex(0)).doClick();
352                     demo.clearToggle = false;
353                 }
354             }
355             thread = null;
356         }
357     } // End DemoControls
358 } // End Balls
359 
360