1 /*
   2  *
   3  * Copyright (c) 2007, 2011, 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 combo;
  75 
  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
  89     public void reset(int w, int h) {
  90         if (w > 400 && h > 100) {
  91             combo.setSelectedIndex(5);
  92         }
  93     }
  94 
  95     @Override
  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
 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 
 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 
 158         public Ball(Color color, int bsize) {
 159             this.color = color;
 160             makeImages(bsize);
 161         }
 162 
 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 
 213         private int blend(int fg, int bg, float fgfactor) {
 214             return (int) (bg + (fg - bg) * fgfactor);
 215         }
 216 
 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")
 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 
 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
 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
 327         public Dimension getPreferredSize() {
 328             return new Dimension(200, 40);
 329         }
 330 
 331         @Override
 332         @SuppressWarnings("SleepWhileHoldingLock")
 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