Java/Advanced Graphics/Animation
Содержание
Animated Graphics
<source lang="java">
import java.awt.Color; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.SwingUtilities; import javax.swing.Timer; /*
* AnimatedGraphics_2008.java * * Created on May 2, 2007, 4:02 PM * * Copyright (c) 2007, Sun Microsystems, Inc * 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 the TimingFramework project 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. */
/**
* * @author Chet */
public class AnimatedGraphics_2008 extends JComponent implements ActionListener {
Color startColor = Color.GRAY; // where we start Color endColor = Color.BLACK; // where we end Color currentColor = startColor; int animationDuration = 2000; // each animation will take 2 seconds long animStartTime; // start time for each animation /** * Set up and start the timer */ public AnimatedGraphics_2008() { Timer timer = new Timer(30, this); // initial delay while window gets set up timer.setInitialDelay(1000); animStartTime = 1000 + System.nanoTime() / 1000000; timer.start(); } /** * Erase to the background color and fill an oval with the current * color (which is being animated elsewhere) */ public void paintComponent(Graphics g) { g.setColor(getBackground()); g.fillRect(0, 0, getWidth(), getHeight()); g.setColor(currentColor); g.fillOval(0, 0, getWidth(), getHeight()); } /** * Callback from the Swing Timer. Calculate the fraction elapsed of * our desired animation duration and interpolate between our start and * end colors accordingly. */ public void actionPerformed(ActionEvent ae) { // calculate elapsed fraction of animation long currentTime = System.nanoTime() / 1000000; long totalTime = currentTime - animStartTime; if (totalTime > animationDuration) { animStartTime = currentTime; } float fraction = (float)totalTime / animationDuration; fraction = Math.min(1.0f, fraction); // interpolate between start and end colors with current fraction int red = (int)(fraction * endColor.getRed() + (1 - fraction) * startColor.getRed()); int green = (int)(fraction * endColor.getGreen() + (1 - fraction) * startColor.getGreen()); int blue = (int)(fraction * endColor.getBlue() + (1 - fraction) * startColor.getBlue()); // set our new color appropriately currentColor = new Color(red, green, blue); // force a repaint to display our oval with its new color repaint(); } private static void createAndShowGUI() { JFrame f = new JFrame("Animated Graphics"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setSize(200, 200); f.add(new AnimatedGraphics_2008()); f.setVisible(true); } public static void main(String args[]) { Runnable doCreateAndShowGUI = new Runnable() { public void run() { createAndShowGUI(); } }; SwingUtilities.invokeLater(doCreateAndShowGUI); }
}
</source>
Background Animation
<source lang="java">
/*
* Copyright (c) 2007, Romain Guy * 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 the TimingFramework project 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. */
import java.awt.AlphaComposite; import java.awt.BorderLayout; import java.awt.Color; import java.awt.ruponent; import java.awt.ruposite; import java.awt.Container; import java.awt.Cursor; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.GradientPaint; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GridBagLayout; import java.awt.HeadlessException; import java.awt.Image; import java.awt.Insets; import java.awt.LayoutManager; import java.awt.LayoutManager2; import java.awt.Paint; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ruponentAdapter; import java.awt.event.ruponentEvent; import java.awt.event.ruponentListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.imageio.ImageIO; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSeparator; import javax.swing.JSlider; import javax.swing.SwingUtilities; import javax.swing.Timer; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; /**
* @author Romain Guy */
public class BackgroundAnimation_2008 extends JFrame {
private AvatarChooser chooser; private CurvesPanel curves; public BackgroundAnimation_2008() throws HeadlessException { super("Stack Layout"); buildContentPane(); // buildDebugControls(); startAnimation(); setSize(640, 400); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } private void startAnimation() { Timer timer = new Timer(50, new ActionListener() { public void actionPerformed(ActionEvent e) { curves.animate(); curves.repaint(); } }); timer.start(); } private void buildDebugControls() { JPanel pane = new JPanel(new BorderLayout()); pane.setBackground(Color.WHITE); pane.add(new JSeparator(), BorderLayout.NORTH); final GraphPanel grapher = new GraphPanel(); JPanel graphPane = new JPanel(new FlowLayout(FlowLayout.CENTER)); graphPane.setOpaque(false); graphPane.add(grapher); JPanel buttonsPane = new JPanel(new FlowLayout(FlowLayout.CENTER)); buttonsPane.setOpaque(false); JSlider spacing = new JSlider(JSlider.HORIZONTAL, 0, 100, 40); spacing.setBackground(Color.WHITE); spacing.setPreferredSize(new Dimension(95, spacing.getPreferredSize().height)); spacing.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { JSlider slider = (JSlider) e.getSource(); double spacing = slider.getValue() / 100.0; chooser.setSpacing(spacing); grapher.spacing = spacing; grapher.repaint(); } }); JSlider sigma = new JSlider(JSlider.HORIZONTAL, 0, 100, 50); sigma.setBackground(Color.WHITE); sigma.setPreferredSize(new Dimension(95, sigma.getPreferredSize().height)); sigma.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { JSlider slider = (JSlider) e.getSource(); double sigma = slider.getValue() / 100.0; chooser.setSigma(sigma); grapher.sigma = sigma; grapher.repaint(); } }); JSlider position = new JSlider(JSlider.HORIZONTAL, -100, 100, 0); position.setBackground(Color.WHITE); position.setPreferredSize(new Dimension(95, position.getPreferredSize().height)); position.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { JSlider slider = (JSlider) e.getSource(); double position = slider.getValue() / 100.0; chooser.setPosition(position); grapher.position = position; grapher.repaint(); } }); JSlider amount = new JSlider(JSlider.HORIZONTAL, 1, 15, 5); amount.setBackground(Color.WHITE); amount.setPreferredSize(new Dimension(95, position.getPreferredSize().height)); amount.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { JSlider slider = (JSlider) e.getSource(); int amount = slider.getValue(); chooser.setAmount(amount); grapher.amount = amount; grapher.repaint(); } }); buttonsPane.add(new JLabel("Spacing: ")); buttonsPane.add(spacing); buttonsPane.add(new JLabel("Sigma: ")); buttonsPane.add(sigma); buttonsPane.add(new JLabel("Position: ")); buttonsPane.add(position); buttonsPane.add(new JLabel("Amount: ")); buttonsPane.add(amount); pane.add(buttonsPane, BorderLayout.NORTH); pane.add(graphPane, BorderLayout.CENTER); add(pane, BorderLayout.SOUTH); } private void buildContentPane() { JPanel pane = new JPanel(); pane.setLayout(new StackLayout()); GradientPanel gradient = new GradientPanel(); chooser = new AvatarChooser(); curves = new CurvesPanel(); pane.add(gradient, StackLayout.TOP); pane.add(chooser, StackLayout.TOP); pane.add(curves, StackLayout.TOP); add(pane); } private class GraphPanel extends JComponent { private double spacing = 0.4; private double position = 0.0; private double sigma = 0.5; private int amount = 5; @Override public Dimension getPreferredSize() { return new Dimension(200, 60); } @Override public boolean isOpaque() { return false; } @Override protected void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setColor(Color.BLACK); g2.drawLine(0, 50, 100, 50); g2.drawLine(50, 0, 50, 60); g2.setColor(Color.BLUE); double lastY = 50.0; for (int x = 0; x < 100; x++) { double y = chooser.ruputeModifier((50.0 - x) / 25.0) * 45.0 + 10; if (x == 0) { lastY = y; } g2.drawLine(x - 1, 60 - (int) lastY, x, 60 - (int) y); lastY = y; } g2.setColor(Color.RED); for (int i = 0; i < amount; i++) { double offset = ((amount / 2) - i) * spacing; double x = (100.0 - 5.0) / 2.0; x += 25.0 * (position + offset); if (x > 100) { continue; } double y = 60.0 - (chooser.ruputeModifier(position + offset) * 45.0 + 10); g2.fill(new Rectangle2D.Double(x, y - 1, 5.0, 5.0)); } g2.setColor(Color.GREEN.darker()); g2.drawLine(25, 0, 25, 60); g2.drawLine(75, 0, 75, 60); g2.setColor(Color.DARK_GRAY); g2.drawString("Sigma: " + sigma, 110.0f, 16.0f); g2.drawString("Spacing: " + spacing, 110.0f, 30.0f); g2.drawString("Position: " + position, 110.0f, 44.0f); } } public static void main(String[] args) { try { UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); } catch (ClassNotFoundException ex) { ex.printStackTrace(); } catch (IllegalAccessException ex) { ex.printStackTrace(); } catch (InstantiationException ex) { ex.printStackTrace(); } catch (UnsupportedLookAndFeelException ex) { ex.printStackTrace(); } SwingUtilities.invokeLater(new Runnable() { public void run() { BackgroundAnimation_2008 tester = new BackgroundAnimation_2008(); tester.setVisible(true); } }); }
} /*
* Copyright (c) 2007, Romain Guy 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 the * TimingFramework project 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. */
class AvatarChooser extends JPanel {
private static final double ANIM_SCROLL_DELAY = 450; private List<Image> avatars = null; private boolean loadingDone = false; private Thread picturesFinder = null; private Timer scrollerTimer = null; private Timer faderTimer = null; private Timer passwordTimer; private float veilAlphaLevel = 0.0f; private float alphaLevel = 0.0f; private float textAlphaLevel = 0.0f; private int avatarIndex; private double avatarPosition = 0.0; private double avatarSpacing = 0.4; private int avatarAmount = 5; private double sigma; private double rho; private double exp_multiplier; private double exp_member; private boolean damaged = true; private DrawableAvatar[] drawableAvatars; private String textAvatar; private FocusGrabber focusGrabber; private AvatarScroller avatarScroller; private CursorChanger cursorChanger; private MouseWheelScroller wheelScroller; private KeyScroller keyScroller; public AvatarChooser() { GridBagLayout layout = new GridBagLayout(); setLayout(layout); findAvatars(); setSigma(0.5); addComponentListener(new DamageManager()); initInputListeners(); addInputListeners(); } public void setAmount(int amount) { if (amount > avatars.size()) { throw new IllegalArgumentException("Too many avatars"); } this.avatarAmount = amount; repaint(); } // XXX package access for debugging purpose only void setPosition(double position) { this.avatarPosition = position; this.damaged = true; repaint(); } public void setSigma(double sigma) { this.sigma = sigma; this.rho = 1.0; computeEquationParts(); this.rho = computeModifierUnprotected(0.0); computeEquationParts(); this.damaged = true; repaint(); } public void setSpacing(double avatarSpacing) { if (avatarSpacing < 0.0 || avatarSpacing > 1.0) { throw new IllegalArgumentException("Spacing must be < 1.0 and > 0.0"); } this.avatarSpacing = avatarSpacing; this.damaged = true; repaint(); } @Override public Dimension getPreferredSize() { return new Dimension(128 * 3, 128 * 2); } @Override public Dimension getMaximumSize() { return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); } @Override public boolean isOpaque() { return false; } @Override public boolean isFocusable() { return true; } @Override protected void paintChildren(Graphics g) { Graphics2D g2 = (Graphics2D) g; Composite oldComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, veilAlphaLevel)); super.paintChildren(g); g2.setComposite(oldComposite); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); if (!loadingDone && faderTimer == null) { return; } Insets insets = getInsets(); int x = insets.left; int y = insets.top; int width = getWidth() - insets.left - insets.right; int height = getHeight() - insets.top - insets.bottom; Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Composite oldComposite = g2.getComposite(); if (damaged) { drawableAvatars = sortAvatarsByDepth(x, y, width, height); damaged = false; } drawAvatars(g2, drawableAvatars); if (drawableAvatars.length > 0) { drawAvatarName(g2); } g2.setComposite(oldComposite); } private void drawAvatars(Graphics2D g2, DrawableAvatar[] drawableAvatars) { for (DrawableAvatar avatar : drawableAvatars) { AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) avatar .getAlpha()); g2.setComposite(composite); g2.drawImage(avatars.get(avatar.getIndex()), (int) avatar.getX(), (int) avatar.getY(), avatar .getWidth(), avatar.getHeight(), null); } } private DrawableAvatar[] sortAvatarsByDepth(int x, int y, int width, int height) { List<DrawableAvatar> drawables = new LinkedList<DrawableAvatar>(); for (int i = 0; i < avatars.size(); i++) { promoteAvatarToDrawable(drawables, x, y, width, height, i - avatarIndex); } DrawableAvatar[] drawableAvatars = new DrawableAvatar[drawables.size()]; drawableAvatars = drawables.toArray(drawableAvatars); Arrays.sort(drawableAvatars); return drawableAvatars; } private void drawAvatarName(Graphics2D g2) { Composite composite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, textAlphaLevel)); double bulletWidth = 150.0; double bulletHeight = 30.0; double x = (getWidth() - bulletWidth) / 2.0; double y = (getHeight() - 164) / 2.0 - bulletHeight * 1.4; drawAvatarBullet(g2, x, y, bulletWidth, bulletHeight); drawAvatarText(g2, y, bulletHeight); g2.setComposite(composite); } private void drawAvatarText(Graphics2D g2, double y, double bulletHeight) { FontRenderContext context = g2.getFontRenderContext(); Font font = new Font("Dialog", Font.PLAIN, 18); TextLayout layout = new TextLayout(textAvatar, font, context); Rectangle2D bounds = layout.getBounds(); float text_x = (float) ((getWidth() - bounds.getWidth()) / 2.0); float text_y = (float) (y + (bulletHeight - layout.getAscent() - layout.getDescent()) / 2.0) + layout.getAscent() - layout.getLeading(); g2.setColor(Color.BLACK); layout.draw(g2, text_x, text_y + 1); g2.setColor(Color.WHITE); layout.draw(g2, text_x, text_y); } private void drawAvatarBullet(Graphics2D g2, double x, double y, double bulletWidth, double bulletHeight) { RoundRectangle2D bullet = new RoundRectangle2D.Double(0.0, 0.0, bulletWidth, bulletHeight, bulletHeight, bulletHeight); Ellipse2D curve = new Ellipse2D.Double(-20.0, bulletHeight / 2.0, bulletWidth + 40.0, bulletHeight); g2.translate(x, y); g2.translate(-1, -2); g2.setColor(new Color(0, 0, 0, 170)); g2.fill(new RoundRectangle2D.Double(0.0, 0.0, bulletWidth + 2, bulletHeight + 4, bulletHeight + 4, bulletHeight + 4)); g2.translate(1, 2); Color startColor = new Color(10, 0, 40); Color endColor = new Color(175, 165, 225); Paint paint = g2.getPaint(); g2.setPaint(new GradientPaint(0.0f, 0.0f, startColor, 0.0f, (float) bulletHeight, endColor)); g2.fill(bullet); startColor = new Color(5, 0, 50); endColor = new Color(105, 100, 155); g2.setPaint(new GradientPaint(0.0f, 0.0f, startColor, 0.0f, (float) bulletHeight, endColor)); Area area = new Area(bullet); area.intersect(new Area(curve)); g2.fill(area); g2.setPaint(paint); g2.translate(-x, -y); } private void promoteAvatarToDrawable(List<DrawableAvatar> drawables, int x, int y, int width, int height, int offset) { double spacing = offset * avatarSpacing; double avatarPosition = this.avatarPosition + spacing; if (avatarIndex + offset < 0 || avatarIndex + offset >= avatars.size()) { return; } Image avatar = avatars.get(avatarIndex + offset); int avatarWidth = avatar.getWidth(null); int avatarHeight = avatar.getHeight(null); double result = computeModifier(avatarPosition); int newWidth = (int) (avatarWidth * result); if (newWidth == 0) { return; } int newHeight = (int) (avatarHeight * result); if (newHeight == 0) { return; } double avatar_x = x + (width - newWidth) / 2.0; double avatar_y = y + (height - newHeight / 2.0) / 2.0; double semiWidth = width / 2.0; avatar_x += avatarPosition * semiWidth; if (avatar_x >= width || avatar_x < -newWidth) { return; } drawables.add(new DrawableAvatar(avatarIndex + offset, avatar_x, avatar_y, newWidth, newHeight, avatarPosition, result)); } private void startFader() { faderTimer = new Timer(35, new FaderAction()); faderTimer.start(); } private void computeEquationParts() { exp_multiplier = Math.sqrt(2.0 * Math.PI) / sigma / rho; exp_member = 4.0 * sigma * sigma; } // XXX package access for debug purpose only double computeModifier(double x) { double result = computeModifierUnprotected(x); if (result > 1.0) { result = 1.0; } else if (result < -1.0) { result = -1.0; } return result; } private double computeModifierUnprotected(double x) { return exp_multiplier * Math.exp((-x * x) / exp_member); } private void addInputListeners() { addMouseListener(focusGrabber); addMouseListener(avatarScroller); addMouseMotionListener(cursorChanger); addMouseWheelListener(wheelScroller); addKeyListener(keyScroller); } private void initInputListeners() { // input listeners are all stateless // hence they can be instantiated once focusGrabber = new FocusGrabber(); avatarScroller = new AvatarScroller(); cursorChanger = new CursorChanger(); wheelScroller = new MouseWheelScroller(); keyScroller = new KeyScroller(); } private void removeInputListeners() { removeMouseListener(focusGrabber); removeMouseListener(avatarScroller); removeMouseMotionListener(cursorChanger); removeMouseWheelListener(wheelScroller); removeKeyListener(keyScroller); } private void findAvatars() { avatars = new ArrayList<Image>(); picturesFinder = new Thread(new PicturesFinderThread()); picturesFinder.start(); } private void setAvatarIndex(int index) { avatarIndex = index; textAvatar = "Photo " + index; } private void scrollBy(int increment) { if (loadingDone) { setAvatarIndex(avatarIndex + increment); if (avatarIndex < 0) { setAvatarIndex(0); } else if (avatarIndex >= avatars.size()) { setAvatarIndex(avatars.size() - 1); } damaged = true; repaint(); } } private void scrollAndAnimateBy(int increment) { if (loadingDone && (scrollerTimer == null || !scrollerTimer.isRunning())) { int index = avatarIndex + increment; if (index < 0) { index = 0; } else if (index >= avatars.size()) { index = avatars.size() - 1; } DrawableAvatar drawable = null; for (DrawableAvatar avatar : drawableAvatars) { if (avatar.index == index) { drawable = avatar; break; } } if (drawable != null) { scrollAndAnimate(drawable); } } } private void scrollAndAnimate(DrawableAvatar avatar) { if (loadingDone) { scrollerTimer = new Timer(10, new AutoScroller(avatar)); scrollerTimer.start(); } } private BufferedImage createReflectedPicture(BufferedImage avatar) { int avatarWidth = avatar.getWidth(); int avatarHeight = avatar.getHeight(); BufferedImage alphaMask = createGradientMask(avatarWidth, avatarHeight); return createReflectedPicture(avatar, alphaMask); } private BufferedImage createReflectedPicture(BufferedImage avatar, BufferedImage alphaMask) { int avatarWidth = avatar.getWidth(); int avatarHeight = avatar.getHeight(); BufferedImage buffer = createReflection(avatar, avatarWidth, avatarHeight); applyAlphaMask(buffer, alphaMask, avatarWidth, avatarHeight); return buffer; } private void applyAlphaMask(BufferedImage buffer, BufferedImage alphaMask, int avatarWidth, int avatarHeight) { Graphics2D g2 = buffer.createGraphics(); g2.setComposite(AlphaComposite.DstOut); g2.drawImage(alphaMask, null, 0, avatarHeight); g2.dispose(); } private BufferedImage createReflection(BufferedImage avatar, int avatarWidth, int avatarHeight) { BufferedImage buffer = new BufferedImage(avatarWidth, avatarHeight << 1, BufferedImage.TYPE_INT_ARGB); Graphics2D g = buffer.createGraphics(); g.drawImage(avatar, null, null); g.translate(0, avatarHeight << 1); AffineTransform reflectTransform = AffineTransform.getScaleInstance(1.0, -1.0); g.drawImage(avatar, reflectTransform, null); g.translate(0, -(avatarHeight << 1)); g.dispose(); return buffer; } private BufferedImage createGradientMask(int avatarWidth, int avatarHeight) { BufferedImage gradient = new BufferedImage(avatarWidth, avatarHeight, BufferedImage.TYPE_INT_ARGB); Graphics2D g = gradient.createGraphics(); GradientPaint painter = new GradientPaint(0.0f, 0.0f, new Color(1.0f, 1.0f, 1.0f, 0.5f), 0.0f, avatarHeight / 2.0f, new Color(1.0f, 1.0f, 1.0f, 1.0f)); g.setPaint(painter); g.fill(new Rectangle2D.Double(0, 0, avatarWidth, avatarHeight)); g.dispose(); return gradient; } private DrawableAvatar getHitAvatar(int x, int y) { for (DrawableAvatar avatar : drawableAvatars) { Rectangle hit = new Rectangle((int) avatar.getX(), (int) avatar.getY(), avatar.getWidth(), avatar.getHeight() / 2); if (hit.contains(x, y)) { return avatar; } } return null; } private class PicturesFinderThread implements Runnable { public void run() { UnixGlobFileFilter allPngs = new UnixGlobFileFilter("*.jpg"); try { FileTreeWalker walker = new FileTreeWalker(new File("."), allPngs); PictureLoader loader = new PictureLoader(); walker.walk(loader); List<File> files = loader.getFilesList(); for (int i = 0; i < files.size(); i++) { BufferedImage image = ImageIO.read(files.get(i)); avatars.add(createReflectedPicture(image)); if (i == (files.size() / 2) + avatarAmount / 2) { setAvatarIndex(i - avatarAmount / 2); startFader(); } } } catch (IOException e) { } loadingDone = true; } private class PictureLoader implements FileTreeWalk { private List<File> filesList; private PictureLoader() { filesList = new ArrayList<File>(); } private List<File> getFilesList() { Collections.sort(filesList); return filesList; } public void walk(File path) { filesList.add(path); } } } private class FaderAction implements ActionListener { private long start = 0; private FaderAction() { alphaLevel = 0.0f; textAlphaLevel = 0.0f; } public void actionPerformed(ActionEvent e) { if (start == 0) { start = System.currentTimeMillis(); } alphaLevel = (System.currentTimeMillis() - start) / 500.0f; textAlphaLevel = alphaLevel; if (alphaLevel > 1.0f) { alphaLevel = 1.0f; textAlphaLevel = 1.0f; faderTimer.stop(); } repaint(); } } private class DrawableAvatar implements Comparable { private int index; private double x; private double y; private int width; private int height; private double zOrder; private double position; private DrawableAvatar(int index, double x, double y, int width, int height, double position, double zOrder) { this.index = index; this.x = x; this.y = y; this.width = width; this.height = height; this.position = position; this.zOrder = zOrder; } public int compareTo(Object o) { double zOrder2 = ((DrawableAvatar) o).zOrder; if (zOrder < zOrder2) { return -1; } else if (zOrder > zOrder2) { return 1; } return 0; } public double getPosition() { return position; } public double getAlpha() { return zOrder * alphaLevel; } public int getHeight() { return height; } public int getIndex() { return index; } public int getWidth() { return width; } public double getX() { return x; } public double getY() { return y; } } private class MouseWheelScroller implements MouseWheelListener { public void mouseWheelMoved(MouseWheelEvent e) { int increment = e.getWheelRotation(); scrollAndAnimateBy(increment); } } private class KeyScroller extends KeyAdapter { @Override public void keyPressed(KeyEvent e) { int keyCode = e.getKeyCode(); switch (keyCode) { case KeyEvent.VK_LEFT: case KeyEvent.VK_UP: scrollAndAnimateBy(-1); break; case KeyEvent.VK_RIGHT: case KeyEvent.VK_DOWN: scrollAndAnimateBy(1); break; case KeyEvent.VK_END: scrollBy(avatars.size() - avatarIndex - 1); break; case KeyEvent.VK_HOME: scrollBy(-avatarIndex - 1); break; case KeyEvent.VK_PAGE_UP: scrollAndAnimateBy(-avatarAmount / 2); break; case KeyEvent.VK_PAGE_DOWN: scrollAndAnimateBy(avatarAmount / 2); break; } } } private class FocusGrabber extends MouseAdapter { @Override public void mouseClicked(MouseEvent e) { requestFocus(); } } private class AvatarScroller extends MouseAdapter { @Override public void mouseClicked(MouseEvent e) { if ((faderTimer != null && faderTimer.isRunning()) || (scrollerTimer != null && scrollerTimer.isRunning()) || drawableAvatars == null) { return; } if (e.getButton() == MouseEvent.BUTTON1) { DrawableAvatar avatar = getHitAvatar(e.getX(), e.getY()); if (avatar != null && avatar.getIndex() != avatarIndex) { scrollAndAnimate(avatar); } } } } private class DamageManager extends ComponentAdapter { @Override public void componentResized(ComponentEvent e) { damaged = true; } } private class AutoScroller implements ActionListener { private double position; private int index; private long start; private AutoScroller(DrawableAvatar avatar) { this.index = avatar.getIndex(); this.position = avatar.getPosition(); this.start = System.currentTimeMillis(); } public void actionPerformed(ActionEvent e) { long elapsed = System.currentTimeMillis() - start; if (elapsed < ANIM_SCROLL_DELAY / 2.0) { textAlphaLevel = (float) (1.0 - 2.0 * (elapsed / ANIM_SCROLL_DELAY)); } else { textAlphaLevel = (float) (((elapsed / ANIM_SCROLL_DELAY) - 0.5) * 2.0); if (textAlphaLevel > 1.0f) { textAlphaLevel = 1.0f; } } if (textAlphaLevel < 0.1f) { textAlphaLevel = 0.1f; textAvatar = "LoginName" + index; } double newPosition = (elapsed / ANIM_SCROLL_DELAY) * -position; if (elapsed >= ANIM_SCROLL_DELAY) { ((Timer) e.getSource()).stop(); setAvatarIndex(index); setPosition(0.0); return; } setPosition(newPosition); } } private class CursorChanger extends MouseMotionAdapter { @Override public void mouseMoved(MouseEvent e) { if ((scrollerTimer != null && scrollerTimer.isRunning()) || drawableAvatars == null) { return; } DrawableAvatar avatar = getHitAvatar(e.getX(), e.getY()); if (avatar != null) { getParent().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); } else { getParent().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } } }
} /*
* Copyright (c) 2007, Romain Guy 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 the * TimingFramework project 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. */
class CurvesPanel extends JPanel {
protected RenderingHints hints; protected int counter = 0; protected Color start = new Color(255, 255, 255, 200); protected Color end = new Color(255, 255, 255, 0); public CurvesPanel() { this(new BorderLayout()); } public CurvesPanel(LayoutManager manager) { super(manager); hints = createRenderingHints(); } protected RenderingHints createRenderingHints() { RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); hints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); return hints; } public void animate() { counter++; } @Override public boolean isOpaque() { return false; } @Override protected void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g; RenderingHints oldHints = g2.getRenderingHints(); g2.setRenderingHints(hints); float width = getWidth(); float height = getHeight(); g2.translate(0, -30); drawCurve(g2, 20.0f, -10.0f, 20.0f, -10.0f, width / 2.0f - 40.0f, 10.0f, 0.0f, -5.0f, width / 2.0f + 40, 1.0f, 0.0f, 5.0f, 50.0f, 5.0f, false); g2.translate(0, 30); g2.translate(0, height - 60); drawCurve(g2, 30.0f, -15.0f, 50.0f, 15.0f, width / 2.0f - 40.0f, 1.0f, 15.0f, -25.0f, width / 2.0f, 1.0f / 2.0f, 0.0f, 25.0f, 15.0f, 9.0f, false); g2.translate(0, -height + 60); drawCurve(g2, height - 35.0f, -5.0f, height - 50.0f, 10.0f, width / 2.0f - 40.0f, 1.0f, height - 35.0f, -25.0f, width / 2.0f, 1.0f / 2.0f, height - 20.0f, 25.0f, 25.0f, 7.0f, true); g2.setRenderingHints(oldHints); } protected void drawCurve(Graphics2D g2, float y1, float y1_offset, float y2, float y2_offset, float cx1, float cx1_offset, float cy1, float cy1_offset, float cx2, float cx2_offset, float cy2, float cy2_offset, float thickness, float speed, boolean invert) { float width = getWidth(); float offset = (float) Math.sin(counter / (speed * Math.PI)); float start_x = 0.0f; float start_y = offset * y1_offset + y1; float end_x = width; float end_y = offset * y2_offset + y2; float ctrl1_x = offset * cx1_offset + cx1; float ctrl1_y = offset * cy1_offset + cy1; float ctrl2_x = offset * cx2_offset + cx2; float ctrl2_y = offset * cy2_offset + cy2; GeneralPath thickCurve = new GeneralPath(); thickCurve.moveTo(start_x, start_y); thickCurve.curveTo(ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y, end_x, end_y); thickCurve.lineTo(end_x, end_y + thickness); thickCurve.curveTo(ctrl2_x, ctrl2_y + thickness, ctrl1_x, ctrl1_y + thickness, start_x, start_y + thickness); thickCurve.lineTo(start_x, start_y); Rectangle bounds = thickCurve.getBounds(); if (!bounds.intersects(g2.getClipBounds())) { return; } GradientPaint painter = new GradientPaint(0, bounds.y, invert ? end : start, 0, bounds.y + bounds.height, invert ? start : end); Paint oldPainter = g2.getPaint(); g2.setPaint(painter); g2.fill(thickCurve); g2.setPaint(oldPainter); }
} /*
* Copyright (c) 2007, Romain Guy 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 the * TimingFramework project 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. */
class GradientPanel extends JPanel {
protected BufferedImage gradientImage; protected Color gradientStart = new Color(204, 249, 124); protected Color gradientEnd = new Color(174, 222, 94); public GradientPanel() { this(new BorderLayout()); } public GradientPanel(LayoutManager layout) { super(layout); addComponentListener(new GradientCacheManager()); } @Override protected void paintComponent(Graphics g) { createImageCache(); if (gradientImage != null) { g.drawImage(gradientImage, 0, 0, getWidth(), getHeight(), null); } } protected void createImageCache() { int width = 2; int height = getHeight(); if (width == 0 || height == 0) { return; } if (gradientImage == null || width != gradientImage.getWidth() || height != gradientImage.getHeight()) { gradientImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = gradientImage.createGraphics(); GradientPaint painter = new GradientPaint(0, 0, gradientEnd, 0, height / 2, gradientStart); g2.setPaint(painter); Rectangle2D rect = new Rectangle2D.Double(0, 0, width, height / 2.0); g2.fill(rect); painter = new GradientPaint(0, height / 2, gradientStart, 0, height, gradientEnd); g2.setPaint(painter); rect = new Rectangle2D.Double(0, (height / 2.0) - 1.0, width, height); g2.fill(rect); g2.dispose(); } } private void disposeImageCache() { synchronized (gradientImage) { gradientImage.flush(); gradientImage = null; } } private class GradientCacheManager implements ComponentListener { public void componentResized(ComponentEvent e) { } public void componentMoved(ComponentEvent e) { } public void componentShown(ComponentEvent e) { } public void componentHidden(ComponentEvent e) { disposeImageCache(); } }
} /*
* Copyright (c) 2007, Romain Guy 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 the * TimingFramework project 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. */
/**
* * @author Romain Guy <romain.guy@mac.ru> */
class StackLayout implements LayoutManager2 {
public static final String BOTTOM = "bottom"; public static final String TOP = "top"; private List<Component> components = new LinkedList<Component>(); public void addLayoutComponent(Component comp, Object constraints) { synchronized (comp.getTreeLock()) { if (BOTTOM.equals(constraints)) { components.add(0, comp); } else if (TOP.equals(constraints)) { components.add(comp); } else { components.add(comp); } } } public void addLayoutComponent(String name, Component comp) { addLayoutComponent(comp, TOP); } public void removeLayoutComponent(Component comp) { synchronized (comp.getTreeLock()) { components.remove(comp); } } public float getLayoutAlignmentX(Container target) { return 0.5f; } public float getLayoutAlignmentY(Container target) { return 0.5f; } public void invalidateLayout(Container target) { } public Dimension preferredLayoutSize(Container parent) { synchronized (parent.getTreeLock()) { int width = 0; int height = 0; for (Component comp : components) { Dimension size = comp.getPreferredSize(); width = Math.max(size.width, width); height = Math.max(size.height, height); } Insets insets = parent.getInsets(); width += insets.left + insets.right; height += insets.top + insets.bottom; return new Dimension(width, height); } } public Dimension minimumLayoutSize(Container parent) { synchronized (parent.getTreeLock()) { int width = 0; int height = 0; for (Component comp : components) { Dimension size = comp.getMinimumSize(); width = Math.max(size.width, width); height = Math.max(size.height, height); } Insets insets = parent.getInsets(); width += insets.left + insets.right; height += insets.top + insets.bottom; return new Dimension(width, height); } } public Dimension maximumLayoutSize(Container target) { return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); } public void layoutContainer(Container parent) { synchronized (parent.getTreeLock()) { int width = parent.getWidth(); int height = parent.getHeight(); Rectangle bounds = new Rectangle(0, 0, width, height); int componentsCount = components.size(); for (int i = 0; i < componentsCount; i++) { Component comp = components.get(i); comp.setBounds(bounds); parent.setComponentZOrder(comp, componentsCount - i - 1); } } }
} /*
* Copyright (c) 2007, Romain Guy 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 the * TimingFramework project 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. */
class FileTreeWalker {
private File path; private static final FileFilter directoryFilter = new FileFilter() { public boolean accept(File pathname) { return pathname.isDirectory(); } }; private FileFilter filter; public FileTreeWalker(File path) throws IOException { this(path, new FileFilter() { public boolean accept(File pathname) { return pathname.isFile(); } }); } public FileTreeWalker(File path, FileFilter filter) throws IOException { if (path == null || !path.exists() || path.isFile()) { throw new IOException("Path " + path + " is not a valid directory."); } this.path = path; this.filter = filter; } public void walk(FileTreeWalk walk) { walkDirectory(walk, path); } private void walkDirectory(FileTreeWalk walk, File dir) { File[] files = dir.listFiles(filter); for (File file : files) { walk.walk(file); } File[] dirs = dir.listFiles(directoryFilter); for (File subDir : dirs) { walkDirectory(walk, subDir); } }
} /*
* Copyright (c) 2007, Romain Guy 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 the * TimingFramework project 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. */
interface FileTreeWalk {
public void walk(File path);
} /*
* Copyright (c) 2007, Romain Guy 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 the * TimingFramework project 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. */
/**
* @author Romain Guy */
class UnixGlobFileFilter implements FileFilter {
private Pattern pattern; public UnixGlobFileFilter(String filter) { pattern = Pattern.rupile(globToRegex(filter)); } public boolean accept(File file) { String path = file.getName(); Matcher matcher = pattern.matcher(path); return matcher.matches(); } private String globToRegex(String glob) { char c = "\0"; boolean escape = false; boolean enclosed = false; StringBuffer buffer = new StringBuffer(glob.length()); for (int i = 0; i < glob.length(); i++) { c = glob.charAt(i); if (escape) { buffer.append("\\"); buffer.append(c); escape = false; continue; } switch (c) { case "*": buffer.append(".").append("*"); break; case "?": buffer.append("."); break; case "\\": escape = true; break; case ".": buffer.append("\\").append("."); break; case "{": buffer.append("("); enclosed = true; break; case "}": buffer.append(")"); enclosed = false; break; case ",": if (enclosed) buffer.append("|"); else buffer.append(","); break; default: buffer.append(c); } } return buffer.toString(); }
}
</source>
Clock
<source lang="java">
import java.util.Calendar; import java.util.Timer; import java.util.TimerTask; import java.awt.*; import javax.swing.*; import no.geosoft.cc.geometry.Geometry; import no.geosoft.cc.geometry.Matrix4x4; import no.geosoft.cc.graphics.*;
/**
* G demo program. Demonstrates: **
-
*
- Advaced geometry generation *
- Dynamic update *
- Threading *
* * @author
Curve Animation
<source lang="java">
import java.awt.*; import java.applet.*; import graph.*; /*************************************************************************
- Applet example0
- Version 1.0 January 1996
-
- Copyright (C) 1996 Leigh Brookshaw
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- /
public class example0 extends Applet implements Runnable {
Graph2D graph; DataSet data; double max; double min; int steps; Thread thread; public void init() { int i; int j; graph = new Graph2D(); setLayout( new BorderLayout() ); add("Center", graph); graph.framecolor = new Color(0,0,0); graph.borderTop = 0; graph.borderBottom = 1; graph.borderLeft = 0; graph.borderRight = 1; graph.setGraphBackground(new Color(50,50,200)); max = 15*Math.PI; min = Math.PI; steps = 20; thread = new Thread(this); thread.start(); } public void run() { DataSet data = null; int count = 1; int direction = 1; double range; Color c = new Color(255,255,0); while(true) { graph.detachDataSet(data); data = null; if(count >= steps ) direction = -1; else if(count <= 0 ) direction = 1; count += direction; range = min + (max-min)*count/steps;
// System.out.println("Range="+range);
data = sinx(range);
// data.xmax = max; // data.xmin = -max;
data.ymax = 1.0; data.linecolor = c; graph.repaint(75); try { thread.sleep(150); } catch(Exception e) { } } }
public DataSet sinx(double range) { int i, j; int np = 100; double data[] = new double[2*np]; double x, y; for(i=j=0; i<np; i++,j+=2) { x = (i-np/2)*range/(np/2); if( x == 0.0 ) y = 1.0; else y = Math.sin(x)/x; data[j] = x; data[j+1] = y; } return graph.loadDataSet(data,np);
} public static void main(String[] a){ javax.swing.JFrame f = new javax.swing.JFrame(); Applet app = new example0(); app.init(); f.getContentPane().add (app); f.pack(); f.setSize (new Dimension (500, 500)); f.setVisible(true); }
}
</source>
Custom move interaction, Switching interactions, Update world extent geometry and Scroll handling
<source lang="java">
import java.awt.*; import java.awt.event.*; import javax.swing.*; import no.geosoft.cc.geometry.Geometry; import no.geosoft.cc.graphics.*;
/**
* G demo program. Demonstrates: **
-
*
- Custom move interaction *
- Switching interactions *
- Update world extent geometry *
- Scroll handling *
* * @author
Moving Button
<source lang="java">
import java.awt.Color; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.Timer; /*
* MovingButton.java * * Created on May 2, 2007, 4:17 PM * * Copyright (c) 2007, Sun Microsystems, Inc * 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 the TimingFramework project 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. */
/**
* * @author Chet */
public class MovingButton extends JButton implements ActionListener {
Timer timer; // for later start/stop actions int animationDuration = 2000; // each animation will take 2 seconds long animStartTime; // start time for each animation int translateY = 0; // y location of the button static final int MAX_Y = 100; /** Creates a new instance of TranslucentButton */ public MovingButton(String label) { super(label); setOpaque(false); timer = new Timer(30, this); addActionListener(this); } /** * Displays our component in the location (0, translateY). Note that * this changes only the rendering location of the button, not the * physical location of it. Note, also, that rendering into g will * be clipped to the physical location of the button, so the button will * disappear as it moves away from that location. */ public void paint(Graphics g) { g.translate(0, translateY); super.paint(g); } /** * This method handles both button clicks, which start/stop the animation, * and Swing Timer events. */ public void actionPerformed(ActionEvent ae) { if (ae.getSource().equals(this)) { // button click if (!timer.isRunning()) { animStartTime = System.nanoTime() / 1000000; this.setText("Stop Animation"); timer.start(); } else { timer.stop(); this.setText("Start Animation"); // reset translation to 0 translateY = 0; } } else { // Timer event // calculate the elapsed fraction long currentTime = System.nanoTime() / 1000000; long totalTime = currentTime - animStartTime; if (totalTime > animationDuration) { animStartTime = currentTime; } float fraction = (float)totalTime / animationDuration; fraction = Math.min(1.0f, fraction); // This calculation will cause translateY to go from 0 to MAX_Y // as the fraction goes from 0 to 1 if (fraction < .5f) { translateY = (int)(MAX_Y * (2 * fraction)); } else { translateY = (int)(MAX_Y * (2 * (1 - fraction))); } // redisplay our component with the new location repaint(); } } private static void createAndShowGUI() { JFrame f = new JFrame("Moving Button"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setSize(300, 300); JPanel checkerboard = new Checkerboard(); checkerboard.add(new MovingButton("Start Animation")); f.add(checkerboard); f.setVisible(true); } public static void main(String args[]) { Runnable doCreateAndShowGUI = new Runnable() { public void run() { createAndShowGUI(); } }; SwingUtilities.invokeLater(doCreateAndShowGUI); } /** * Paints a checkerboard background */ private static class Checkerboard extends JPanel { private static final int DIVISIONS = 10; static final int CHECKER_SIZE = 60; public void paintComponent(Graphics g) { g.setColor(Color.white); g.fillRect(0, 0, getWidth(), getHeight()); g.setColor(Color.BLACK); for (int stripeX = 0; stripeX < getWidth(); stripeX += CHECKER_SIZE) { for (int y = 0, row = 0; y < getHeight(); y += CHECKER_SIZE/2, ++row) { int x = (row % 2 == 0) ? stripeX : (stripeX + CHECKER_SIZE/2); g.fillRect(x, y, CHECKER_SIZE/2, CHECKER_SIZE/2); } } } }
}
</source>
Moving Button Container
<source lang="java">
import java.awt.Color; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.SwingUtilities; import javax.swing.Timer; /*
* MovingButtonContainer.java * * Created on May 2, 2007, 4:39 PM * * Copyright (c) 2007, Sun Microsystems, Inc * 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 the TimingFramework project 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. */
/**
* * @author Chet */
public class MovingButtonContainer extends JComponent implements ActionListener {
Timer timer; // for later start/stop actions int animationDuration = 2000; // each animation will take 2 seconds long animStartTime; // start time for each animation int translateY = 0; // current Y location of button static final int MAX_Y = 100; JButton button = null; private static final int DIVISIONS = 10; static final int CHECKER_SIZE = 60; /** Creates a new instance of TranslucentButton */ public MovingButtonContainer() { setLayout(new java.awt.FlowLayout()); timer = new Timer(30, this); button = new JButton("Start Animation"); // Need setOpaque(false) to force Swing to paint the button"s parent button.setOpaque(false); button.addActionListener(this); add(button); } /** * This method handles both button clicks, which start/stop the animation, * and Swing Timer events, which animate translateY. */ public void actionPerformed(ActionEvent ae) { if (ae.getSource().equals(button)) { // button click if (!timer.isRunning()) { animStartTime = System.nanoTime() / 1000000; button.setText("Stop Animation"); timer.start(); } else { timer.stop(); button.setText("Start Animation"); // reset translation to 0 translateY = 0; // Must force repaint to get final animated position erased repaint(); } } else { // Timer event // calculate elapsed fraction long currentTime = System.nanoTime() / 1000000; long totalTime = currentTime - animStartTime; if (totalTime > animationDuration) { animStartTime = currentTime; } float fraction = (float)totalTime / animationDuration; fraction = Math.min(1.0f, fraction); // This calculation will cause translateY to go from 0 to MAX_Y // as the fraction goes from 0 to 1 if (fraction < .5f) { translateY = (int)(MAX_Y * (2 * fraction)); } else { translateY = (int)(MAX_Y * (2 * (1 - fraction))); } // redisplay our container to paint the button in the new location repaint(); } } /** * Paint our container with a checkerboard background. Then set a * translation factor on the Graphics object and call the superclass, * which will paint the children (e.g, our button) with this * translation. */ public void paint(Graphics g) { g.setColor(Color.white); g.fillRect(0, 0, getWidth(), getHeight()); g.setColor(Color.BLACK); for (int stripeX = 0; stripeX < getWidth(); stripeX += CHECKER_SIZE) { for (int y = 0, row = 0; y < getHeight(); y += CHECKER_SIZE/2, ++row) { int x = (row % 2 == 0) ? stripeX : (stripeX + CHECKER_SIZE/2); g.fillRect(x, y, CHECKER_SIZE/2, CHECKER_SIZE/2); } } // Translate our graphics according to the animated translateY value g.translate(0, translateY); // Defer to superclass to handle painting for component, which includes // painting the children of this container super.paint(g); } private static void createAndShowGUI() { JFrame f = new JFrame("Moving Button Container"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setSize(300, 300); JComponent buttonContainer = new MovingButtonContainer(); f.add(buttonContainer); f.setVisible(true); } public static void main(String args[]) { Runnable doCreateAndShowGUI = new Runnable() { public void run() { createAndShowGUI(); } }; SwingUtilities.invokeLater(doCreateAndShowGUI); }
}
</source>
Scroll Handling
<source lang="java">
import java.awt.*; import java.awt.event.*; import javax.swing.*; import no.geosoft.cc.geometry.Geometry; import no.geosoft.cc.graphics.*;
/**
* G demo program. Demonstrates: **
-
*
- Custom move interaction *
- Switching interactions *
- Update world extent geometry *
- Scroll handling *
* * @author
Simple animation technique
<source lang="java">
import java.awt.*; import javax.swing.*; import no.geosoft.cc.geometry.Geometry; import no.geosoft.cc.geometry.Matrix4x4; import no.geosoft.cc.graphics.*;
/**
* G demo program. Demonstrates: **
-
*
- Simple animation technique *
* * @author
Smooth Moves
<source lang="java">
import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.Image; import java.awt.Transparency; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.image.BufferedImage; import java.net.URL; import javax.imageio.ImageIO; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.SwingUtilities; import javax.swing.Timer; /*
* SmoothMoves.java * * Created on May 2, 2007, 4:49 PM * * Copyright (c) 2007, Sun Microsystems, Inc * 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 the TimingFramework project 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. */
/**
* * @author Chet */
public class SmoothMoves extends JComponent implements ActionListener, KeyListener {
/** image holds the graphics we render for each animating object */ BufferedImage image = null; static int imageW = 100; static int imageH = 150; /** Location of fading animation */ int fadeX = 50; int fadeY = 50; /** X values that moving animation will move between */ static int moveMinX = 150; static int moveMaxX = 350; /** Current x/y location of moving animation */ int moveX = moveMinX; int moveY = 50; /** Current opacity of fading animation */ float opacity = 0.0f; /** Toggles for various demo options (key to toggle in parentheses) */ boolean useImage = false; // (i) image instead of rectangle boolean useAA = false; // (a) anti-aliased edges (rectangle only) boolean motionBlur = false; // (b) ghost images behind moving animation boolean alterColor = false; // (c) light-gray instead of black rectangle boolean linear = true; // (l) linear vs. non-linear motion /** Used for motion blur rendering; holds information for ghost trail */ int blurSize = 5; int prevMoveX[]; int prevMoveY[]; float trailOpacity[]; /** Basic Timer animation info */ final static int CYCLE_TIME = 2000; // One cycle takes 2 seconds int currentResolution = 50; // current Timer resolution Timer timer = null; // animation Timer long cycleStart; // track start time for each cycle /** Creates a new instance of SmoothAnimation */ public SmoothMoves() { // createAnimationImage(); cycleStart = System.nanoTime() / 1000000; startTimer(currentResolution); } /** * Create the image that will be animated. This image may be an actual image * (duke.gif), or some graphics (a variation on a black filled rectangle) that * are rendered into an image. The contents of this image are dependent upon * the runtime toggles that have been set when this method is called. */ void createAnimationImage() { GraphicsConfiguration gc = getGraphicsConfiguration(); image = gc.createCompatibleImage(imageW, imageH, Transparency.TRANSLUCENT); Graphics2D gImg = image.createGraphics(); if (useImage) { try { URL url = getClass().getResource("duke.gif"); Image originalImage = ImageIO.read(url); gImg.drawImage(originalImage, 0, 0, imageW, imageH, null); } catch (Exception e) { } } else { // use graphics Color graphicsColor; if (alterColor) { graphicsColor = Color.LIGHT_GRAY; } else { graphicsColor = Color.BLACK; } gImg.setColor(graphicsColor); gImg.fillRect(0, 0, imageW, imageH); if (useAA) { // Antialiasing hack - just draw a fading-out border around the // rectangle gImg.setComposite(AlphaComposite.Src); int red = graphicsColor.getRed(); int green = graphicsColor.getRed(); int blue = graphicsColor.getRed(); gImg.setColor(new Color(red, green, blue, 50)); gImg.drawRect(0, 0, imageW - 1, imageH - 1); gImg.setColor(new Color(red, green, blue, 100)); gImg.drawRect(1, 1, imageW - 3, imageH - 3); gImg.setColor(new Color(red, green, blue, 150)); gImg.drawRect(2, 2, imageW - 5, imageH - 5); gImg.setColor(new Color(red, green, blue, 200)); gImg.drawRect(3, 3, imageW - 7, imageH - 7); gImg.setColor(new Color(red, green, blue, 225)); gImg.drawRect(4, 4, imageW - 9, imageH - 9); } } gImg.dispose(); } public void paintComponent(Graphics g) { if (image == null) { createAnimationImage(); } // Erase the background g.setColor(Color.WHITE); g.fillRect(0, 0, getWidth(), getHeight()); // Draw the fading image Graphics2D gFade = (Graphics2D) g.create(); gFade.setComposite(AlphaComposite.SrcOver.derive(opacity)); gFade.drawImage(image, fadeX, fadeY, null); gFade.dispose(); // Draw the moving image if (motionBlur) { // Draw previous locations of the image as a trail of // ghost images if (prevMoveX == null) { // blur location array not yet created; create it now prevMoveX = new int[blurSize]; prevMoveY = new int[blurSize]; trailOpacity = new float[blurSize]; float incrementalFactor = .2f / (blurSize + 1); for (int i = 0; i < blurSize; ++i) { // default values, act as flag to not render these // until they have real values prevMoveX[i] = -1; prevMoveY[i] = -1; // vary the translucency by the number of the ghost // image; the further away it is from the current one, // the more faded it will be trailOpacity[i] = (.2f - incrementalFactor) - i * incrementalFactor; } } else { Graphics2D gTrail = (Graphics2D) g.create(); for (int i = 0; i < blurSize; ++i) { if (prevMoveX[i] >= 0) { // Render each blur image with the appropriate // amount of translucency gTrail.setComposite(AlphaComposite.SrcOver.derive(trailOpacity[i])); gTrail.drawImage(image, prevMoveX[i], prevMoveY[i], null); } } gTrail.dispose(); } } g.drawImage(image, moveX, moveY, null); if (motionBlur) { // shift the ghost positions to add the current position and // drop the oldest one for (int i = blurSize - 1; i > 0; --i) { prevMoveX[i] = prevMoveX[i - 1]; prevMoveY[i] = prevMoveY[i - 1]; } prevMoveX[0] = moveX; prevMoveY[0] = moveY; } } /** * This method handles the events from the Swing Timer */ public void actionPerformed(ActionEvent ae) { // calculate the fraction elapsed of the animation and call animate() // to alter the values accordingly long currentTime = System.nanoTime() / 1000000; long totalTime = currentTime - cycleStart; if (totalTime > CYCLE_TIME) { cycleStart = currentTime; } float fraction = (float) totalTime / CYCLE_TIME; fraction = Math.min(1.0f, fraction); fraction = 1 - Math.abs(1 - (2 * fraction)); animate(fraction); } /** * Animate the opacity and location factors, according to the current * fraction. */ public void animate(float fraction) { float animationFactor; if (linear) { animationFactor = fraction; } else { // Our "nonlinear" motion just uses a sin function to get a // simple bounce behavior animationFactor = (float) Math.sin(fraction * (float) Math.PI / 2); } // Clamp the value to make sure it does not exceed the bounds animationFactor = Math.min(animationFactor, 1.0f); animationFactor = Math.max(animationFactor, 0.0f); // The opacity, used by the fading animation, will just use the // animation fraction directly opacity = animationFactor; // The move animation will calculate a location based on a linear // interpolation between its start and end points using the fraction moveX = moveMinX + (int) (.5f + animationFactor * (float) (moveMaxX - moveMinX)); // redisplay our component with the new animated values repaint(); } /** * Moves the frame rate up or down by changing the Timer resolution */ private void changeResolution(boolean faster) { if (faster) { currentResolution -= 5; } else { currentResolution += 5; } currentResolution = Math.max(currentResolution, 0); currentResolution = Math.min(currentResolution, 500); startTimer(currentResolution); } /** * Starts the animation */ private void startTimer(int resolution) { if (timer != null) { timer.stop(); timer.setDelay(resolution); } else { timer = new Timer(resolution, this); } timer.start(); } /** * Toggles various rendering flags */ public void keyPressed(KeyEvent ke) { int keyCode = ke.getKeyCode(); if (keyCode == KeyEvent.VK_B) { // B: Motion blur - displays trail of ghost images motionBlur = !motionBlur; } else if (keyCode == KeyEvent.VK_A) { // A: Antialiasing - Displays soft edges around graphics useAA = !useAA; createAnimationImage(); } else if (keyCode == KeyEvent.VK_C) { // C: Color - Toggles rectangle color between dark and light colors alterColor = !alterColor; createAnimationImage(); } else if (keyCode == KeyEvent.VK_I) { // I: Image - Toggles use of image or filled rectangle to show how // straight edges affect animation perception useImage = !useImage; createAnimationImage(); } else if (keyCode == KeyEvent.VK_UP) { // Up Arrow: Speed - Speeds up frame rate changeResolution(true); } else if (keyCode == KeyEvent.VK_DOWN) { // Down Arrow: Speed - Slows down frame rate changeResolution(false); } else if (keyCode == KeyEvent.VK_L) { // L: Linearity: Toggles linear/nonlinear motion linear = !linear; } else if (keyCode >= KeyEvent.VK_1 && keyCode <= KeyEvent.VK_9) { // 0-9: Blur size: Toggles size of ghost trail for motion blur blurSize = keyCode - KeyEvent.VK_0; prevMoveX = prevMoveY = null; } } // Unused KeyListener implementations public void keyReleased(KeyEvent ke) { } public void keyTyped(KeyEvent ke) { } private static void createAndShowGUI() { JFrame f = new JFrame("Smooth Moves"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setSize(moveMaxX + imageW + 50, 300); SmoothMoves component = new SmoothMoves(); f.add(component); f.setVisible(true); f.addKeyListener(component); } public static void main(String[] args) { Runnable doCreateAndShowGUI = new Runnable() { public void run() { createAndShowGUI(); } }; SwingUtilities.invokeLater(doCreateAndShowGUI); }
}
</source>