/* * * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Oracle nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package java2d.demos.Mix; import static java.awt.Color.BLUE; import static java.awt.Color.GREEN; import static java.awt.Color.ORANGE; import static java.awt.Color.RED; import static java.awt.Color.WHITE; import static java.awt.Color.YELLOW; import static java.lang.Math.random; import static java.lang.Math.sqrt; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.awt.image.IndexColorModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java2d.AnimatingControlsSurface; import java2d.CustomControls; import javax.swing.AbstractButton; import javax.swing.JComboBox; import javax.swing.JToggleButton; import javax.swing.JToolBar; /** * Animated color bouncing balls with custom controls. */ @SuppressWarnings("serial") public class Balls extends AnimatingControlsSurface { private static Color[] colors = { RED, ORANGE, YELLOW, GREEN.darker(), BLUE, new Color(75, 00, 82), new Color(238, 130, 238) }; private long now, deltaT, lasttime; private boolean active; protected Ball[] balls = new Ball[colors.length]; protected boolean clearToggle; protected JComboBox combo; public Balls() { setBackground(WHITE); for (int i = 0; i < colors.length; i++) { balls[i] = new Ball(colors[i], 30); } balls[0].isSelected = true; balls[3].isSelected = true; balls[4].isSelected = true; balls[6].isSelected = true; setControls(new Component[] { new DemoControls(this) }); } @Override public void reset(int w, int h) { if (w > 400 && h > 100) { combo.setSelectedIndex(5); } } @Override public void step(int w, int h) { if (lasttime == 0) { lasttime = System.currentTimeMillis(); } now = System.currentTimeMillis(); deltaT = now - lasttime; active = false; for (Ball ball : balls) { if (ball == null) { return; } ball.step(deltaT, w, h); if (ball.Vy > .02 || -ball.Vy > .02 || ball.y + ball.bsize < h) { active = true; } } if (!active) { for (Ball ball : balls) { ball.Vx = (float) random() / 4.0f - 0.125f; ball.Vy = -(float) random() / 4.0f - 0.2f; } clearToggle = true; } } @Override public void render(int w, int h, Graphics2D g2) { for (Ball b : balls) { if (b == null || b.imgs[b.index] == null || !b.isSelected) { continue; } g2.drawImage(b.imgs[b.index], (int) b.x, (int) b.y, this); } lasttime = now; } public static void main(String[] argv) { createDemoFrame(new Balls()); } protected static final class Ball { public static final int nImgs = 5; public int bsize; public float x, y; public float Vx = 0.1f; public float Vy = 0.05f; public BufferedImage[] imgs; // Pick a random starting image index, but not the last: we're going UP // and that would throw us off the end. public int index = (int) (random() * (nImgs - 1)); private static final float inelasticity = .96f; private static final float Ax = 0.0f; private static final float Ay = 0.0002f; private static final int UP = 0; private static final int DOWN = 1; private int indexDirection = UP; private float jitter; private Color color; private boolean isSelected; public Ball(Color color, int bsize) { this.color = color; makeImages(bsize); } public void makeImages(int bsize) { this.bsize = bsize * 2; int R = bsize; byte[] data = new byte[R * 2 * R * 2]; int maxr = 0; for (int Y = 2 * R; --Y >= 0;) { int x0 = (int) (sqrt(R * R - (Y - R) * (Y - R)) + 0.5); int p = Y * (R * 2) + R - x0; for (int X = -x0; X < x0; X++) { int xx = X + 15; int yy = Y - R + 15; int r = (int) (Math.hypot(xx, yy) + 0.5); if (r > maxr) { maxr = r; } data[p++] = r <= 0 ? 1 : (byte) r; } } imgs = new BufferedImage[nImgs]; int bg = 255; byte[] red = new byte[256]; red[0] = (byte) bg; byte[] green = new byte[256]; green[0] = (byte) bg; byte[] blue = new byte[256]; blue[0] = (byte) bg; for (int r = 0; r < imgs.length; r++) { float b = 0.5f + ((r + 1f) / imgs.length / 2f); for (int i = maxr; i >= 1; --i) { float d = (float) i / maxr; red[i] = (byte) blend(blend(color.getRed(), 255, d), bg, b); green[i] = (byte) blend(blend(color.getGreen(), 255, d), bg, b); blue[i] = (byte) blend(blend(color.getBlue(), 255, d), bg, b); } IndexColorModel icm = new IndexColorModel(8, maxr + 1, red, green, blue, 0); DataBufferByte dbb = new DataBufferByte(data, data.length); int[] bandOffsets = { 0 }; WritableRaster wr = Raster.createInterleavedRaster(dbb, R * 2, R * 2, R * 2, 1, bandOffsets, null); imgs[r] = new BufferedImage(icm, wr, icm.isAlphaPremultiplied(), null); } } private int blend(int fg, int bg, float fgfactor) { return (int) (bg + (fg - bg) * fgfactor); } public void step(long deltaT, int w, int h) { jitter = (float) random() * .01f - .005f; x += Vx * deltaT + (Ax / 2.0) * deltaT * deltaT; y += Vy * deltaT + (Ay / 2.0) * deltaT * deltaT; if (x <= 0.0f) { x = 0.0f; Vx = -Vx * inelasticity + jitter; //collision_x = true; } if (x + bsize >= w) { x = w - bsize; Vx = -Vx * inelasticity + jitter; //collision_x = true; } if (y <= 0) { y = 0; Vy = -Vy * inelasticity + jitter; //collision_y = true; } if (y + bsize >= h) { y = h - bsize; Vx *= inelasticity; Vy = -Vy * inelasticity + jitter; //collision_y = true; } Vy = Vy + Ay * deltaT; Vx = Vx + Ax * deltaT; if (indexDirection == UP) { index++; } if (indexDirection == DOWN) { --index; } if (index + 1 == nImgs) { indexDirection = DOWN; } if (index == 0) { indexDirection = UP; } } } // End class Ball final class DemoControls extends CustomControls implements ActionListener { Balls demo; JToolBar toolbar; @SuppressWarnings("LeakingThisInConstructor") public DemoControls(Balls demo) { super(demo.name); this.demo = demo; add(toolbar = new JToolBar()); toolbar.setFloatable(false); addTool("Clear", true); addTool("R", demo.balls[0].isSelected); addTool("O", demo.balls[1].isSelected); addTool("Y", demo.balls[2].isSelected); addTool("G", demo.balls[3].isSelected); addTool("B", demo.balls[4].isSelected); addTool("I", demo.balls[5].isSelected); addTool("V", demo.balls[6].isSelected); add(combo = new JComboBox<>()); combo.addItem("10"); combo.addItem("20"); combo.addItem("30"); combo.addItem("40"); combo.addItem("50"); combo.addItem("60"); combo.addItem("70"); combo.addItem("80"); combo.setSelectedIndex(2); combo.addActionListener(this); } public void addTool(String str, boolean state) { JToggleButton b = (JToggleButton) toolbar.add(new JToggleButton(str)); b.setFocusPainted(false); b.setSelected(state); b.addActionListener(this); int width = b.getPreferredSize().width; Dimension prefSize = new Dimension(width, 21); b.setPreferredSize(prefSize); b.setMaximumSize(prefSize); b.setMinimumSize(prefSize); } @Override public void actionPerformed(ActionEvent e) { if (e.getSource() instanceof JComboBox) { int size = Integer.parseInt((String) combo.getSelectedItem()); for (Ball ball : demo.balls) { ball.makeImages(size); } return; } JToggleButton b = (JToggleButton) e.getSource(); if (b.getText().equals("Clear")) { demo.clearSurface = b.isSelected(); } else { int index = toolbar.getComponentIndex(b) - 1; demo.balls[index].isSelected = b.isSelected(); } } @Override public Dimension getPreferredSize() { return new Dimension(200, 40); } @Override @SuppressWarnings("SleepWhileHoldingLock") public void run() { try { Thread.sleep(999); } catch (Exception e) { return; } Thread me = Thread.currentThread(); ((AbstractButton) toolbar.getComponentAtIndex(2)).doClick(); while (thread == me) { try { Thread.sleep(222); } catch (InterruptedException e) { return; } if (demo.clearToggle) { if (demo.clearSurface) { combo.setSelectedIndex((int) (random() * 5)); } ((AbstractButton) toolbar.getComponentAtIndex(0)).doClick(); demo.clearToggle = false; } } thread = null; } } // End DemoControls } // End Balls