Java/Advanced Graphics/Animation

Материал из Java эксперт
Версия от 18:01, 31 мая 2010; (обсуждение)
(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск

Animated Graphics

 
 
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);
    }
}





Background Animation

 
 
/*
 * 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();
  }
}





Clock

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:
 *
 * <ul>
 * <li> Advaced geometry generation
 * <li> Dynamic update
 * <li> Threading
 * </ul>
 * 
 * @author 



== Curve Animation ==






   
  <!-- start source code -->
   
    <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);
      }
}





Custom move interaction, Switching interactions, Update world extent geometry and Scroll handling

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:
 *
 * <ul>
 * <li>Custom move interaction
 * <li>Switching interactions
 * <li>Update world extent geometry
 * <li>Scroll handling
 * </ul>
 * 
 * @author 



== Moving Button ==






   
  <!-- start source code -->
   
    <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);
                }
            }
        }
    }
}





Moving Button Container

 
 
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);
    }
}





Scroll Handling

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:
 *
 * <ul>
 * <li>Custom move interaction
 * <li>Switching interactions
 * <li>Update world extent geometry
 * <li>Scroll handling
 * </ul>
 * 
 * @author 



== Simple animation technique ==






   
  <!-- start source code -->
   
    <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:
 *
 * <ul>
 * <li>Simple animation technique
 * </ul>
 * 
 * @author 



== Smooth Moves ==






   
  <!-- start source code -->
   
    <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);
  }
}