/** * Copyright (c) 2006, 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. */ package org.jdesktop.animation.timing.examples.editor; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Image; import java.awt.Insets; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.geom.Point2D; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.IOException; import java.text.NumberFormat; import java.util.Locale; import javax.imageio.ImageIO; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.DefaultListCellRenderer; import javax.swing.DefaultListModel; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSeparator; import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import org.jdesktop.animation.timing.interpolation.KeyFrames; import org.jdesktop.animation.timing.interpolation.KeyTimes; import org.jdesktop.animation.timing.interpolation.KeyValues; import org.jdesktop.animation.timing.interpolation.PropertySetter; import org.jdesktop.animation.timing.Animator; import org.jdesktop.animation.timing.Animator.RepeatBehavior; import org.jdesktop.animation.timing.interpolation.Interpolator; import org.jdesktop.animation.timing.interpolation.Evaluator; import org.jdesktop.animation.timing.interpolation.SplineInterpolator; class SplineControlPanel extends JPanel { private SplineDisplay display; private DropSimulator dropSimulator = new DropSimulator(); private BouncerSimulator bounceSimulator = new BouncerSimulator(); private int linesCount = 0; private JLabel labelControl1; private JLabel labelControl2; private Animator controller; SplineControlPanel() { super(new BorderLayout()); add(buildEquationDisplay(), BorderLayout.CENTER); add(buildDebugControls(), BorderLayout.EAST); } private Component buildDebugControls() { JButton button; JPanel debugPanel = new JPanel(new GridBagLayout()); debugPanel.add(Box.createHorizontalStrut(150), new GridBagConstraints(0, linesCount++, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); // button = addButton(debugPanel, "Create"); // button.addActionListener(new ActionListener() { // public void actionPerformed(ActionEvent e) { // JFileChooser chooser = new JFileChooser("."); // int choice = chooser.showSaveDialog(SplineControlPanel.this); // if (choice == JFileChooser.CANCEL_OPTION) { // return; // } // File file = chooser.getSelectedFile(); // try { // OutputStream out = new FileOutputStream(file); // display.saveAsTemplate(out); // out.close(); // } catch (FileNotFoundException e1) { // } catch (IOException e1) { // } // } // }); addSeparator(debugPanel, "Control Points"); labelControl1 = addDebugLabel(debugPanel, "Point 1:", formatPoint(display.getControl1())); labelControl2 = addDebugLabel(debugPanel, "Point 2:", formatPoint(display.getControl2())); button = addButton(debugPanel, "Copy Code"); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { NumberFormat formatter = getNumberFormatter(); Point2D c1 = display.getControl1(); Point2D c2 = display.getControl2(); StringBuilder code = new StringBuilder(); code.append("Spline spline = new Spline("); code.append(formatter.format(c1.getX())).append("f, "); code.append(formatter.format(c1.getY())).append("f, "); code.append(formatter.format(c2.getX())).append("f, "); code.append(formatter.format(c2.getY())).append("f);"); Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); clipboard.setContents(new StringSelection(code.toString()), null); } }); addEmptySpace(debugPanel, 6); addSeparator(debugPanel, "Animation"); button = addButton(debugPanel, "Play Sample"); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { startSampleAnimation(); } }); addEmptySpace(debugPanel, 6); addSeparator(debugPanel, "Templates"); debugPanel.add(createTemplates(), new GridBagConstraints(0, linesCount++, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); addEmptySpace(debugPanel, 6); debugPanel.add(Box.createVerticalGlue(), new GridBagConstraints(0, linesCount++, 2, 1, 1.0, 1.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); JPanel wrapper = new JPanel(new BorderLayout()); wrapper.add(new JSeparator(JSeparator.VERTICAL), BorderLayout.WEST); wrapper.add(debugPanel); wrapper.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 6)); return wrapper; } private Component createTemplates() { DefaultListModel model = new DefaultListModel(); model.addElement(createTemplate(0.0, 0.0, 1.0, 1.0)); model.addElement(createTemplate(0.0, 1.0, 0.0, 1.0)); model.addElement(createTemplate(0.0, 1.0, 1.0, 1.0)); model.addElement(createTemplate(0.0, 1.0, 1.0, 0.0)); model.addElement(createTemplate(1.0, 0.0, 0.0, 1.0)); model.addElement(createTemplate(1.0, 0.0, 1.0, 1.0)); model.addElement(createTemplate(1.0, 0.0, 1.0, 0.0)); JList list = new JList(model); list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); list.setCellRenderer(new TemplateCellRenderer()); list.addListSelectionListener(new TemplateSelectionHandler()); JScrollPane pane = new JScrollPane(list); pane.getViewport().setPreferredSize(new Dimension(98, 97 * 3)); return pane; } private JButton addButton(JPanel debugPanel, String label) { JButton button; debugPanel.add(button = new JButton(label), new GridBagConstraints(0, linesCount++, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(3, 0, 0, 0), 0, 0)); return button; } private String formatPoint(Point2D p) { NumberFormat formatter = getNumberFormatter(); return "" + formatter.format(p.getX()) + ", " + formatter.format(p.getY()); } private Component buildEquationDisplay() { JPanel panel = new JPanel(new BorderLayout()); display = new SplineDisplay(); display.addPropertyChangeListener("control1", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { labelControl1.setText(formatPoint(display.getControl1())); } }); display.addPropertyChangeListener("control2", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { labelControl2.setText(formatPoint(display.getControl2())); } }); panel.add(display, BorderLayout.NORTH); JPanel wrapper = new JPanel(new GridBagLayout()); wrapper.add(new JSeparator(), new GridBagConstraints(0, 0, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); wrapper.add(bounceSimulator, new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); wrapper.add(dropSimulator, new GridBagConstraints(1, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); panel.add(wrapper, BorderLayout.CENTER); return panel; } private JLabel addDebugLabel(JPanel panel, String label, String value) { JLabel labelComponent = new JLabel(label); panel.add(labelComponent, new GridBagConstraints(0, linesCount, 1, 1, 0.5, 0.0, GridBagConstraints.LINE_END, GridBagConstraints.NONE, new Insets(0, 6, 0, 0), 0, 0)); labelComponent = new JLabel(value); panel.add(labelComponent, new GridBagConstraints(1, linesCount++, 1, 1, 0.5, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 6, 0, 0), 0, 0)); return labelComponent; } private void addEmptySpace(JPanel panel, int size) { panel.add(Box.createVerticalStrut(size), new GridBagConstraints(0, linesCount++, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.VERTICAL, new Insets(6, 0, 0, 0), 0, 0)); } private void addSeparator(JPanel panel, String label) { JPanel innerPanel = new JPanel(new GridBagLayout()); innerPanel.add(new JLabel(label), new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); innerPanel.add(new JSeparator(), new GridBagConstraints(1, 0, 1, 1, 0.9, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 6, 0, 6), 0, 0)); panel.add(innerPanel, new GridBagConstraints(0, linesCount++, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 6, 0), 0, 0)); } private void startSampleAnimation() { if (controller != null && controller.isRunning()) { controller.stop(); } Point2D control1 = display.getControl1(); Point2D control2 = display.getControl2(); Interpolator splines = new SplineInterpolator((float) control1.getX(), (float) control1.getY(), (float) control2.getX(), (float) control2.getY()); KeyTimes times = new KeyTimes(0.0f, 1.0f); KeyValues values = KeyValues.create(0.0, 1.0); KeyFrames frames = new KeyFrames(values,times, splines); PropertySetter dropModifier = new PropertySetter(dropSimulator, "time", frames); PropertySetter bounceModifier = new PropertySetter(bounceSimulator, "time", frames); controller = new Animator(1000, 4, RepeatBehavior.REVERSE, dropModifier); controller.setResolution(10); controller.addTarget(bounceModifier); controller.start(); } private Evaluator point2dInterpolator = new Point2DNonLinearInterpolator(); private class Point2DNonLinearInterpolator extends Evaluator<Point2D> { private Point2D value; public Point2D evaluate(Point2D v0, Point2D v1, float fraction) { Point2D value = (Point2D)v0.clone(); if (v0 != v1) { double x = value.getX(); x += (v1.getX() - v0.getX()) * fraction; double y = value.getY(); y += (v1.getY() - v0.getY()) * fraction; value.setLocation(x, y); } else { value.setLocation(v0.getX(), v0.getY()); } return value; } } private class TemplateSelectionHandler implements ListSelectionListener { public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting()) { return; } JList list = (JList) e.getSource(); Template template = (Template) list.getSelectedValue(); if (template != null) { if (controller != null && controller.isRunning()) { controller.stop(); } controller = new Animator(300, new PropertySetter(display, "control1", point2dInterpolator, display.getControl1(), template.getControl1())); controller.setResolution(10); controller.addTarget(new PropertySetter(display, "control2", point2dInterpolator, display.getControl2(), template.getControl2())); controller.start(); } } } private static NumberFormat getNumberFormatter() { NumberFormat formatter = NumberFormat.getInstance(Locale.ENGLISH); formatter.setMinimumFractionDigits(2); formatter.setMaximumFractionDigits(2); return formatter; } private static Template createTemplate(double x1, double y1, double x2, double y2) { return new Template(new Point2D.Double(x1, y1), new Point2D.Double(x2, y2)); } private static class TemplateCellRenderer extends DefaultListCellRenderer { private boolean isSelected; @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { Template template = (Template) value; this.setBackground(Color.WHITE); this.setIcon(new ImageIcon(template.getImage())); this.isSelected = isSelected; return this; } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); if (isSelected) { g.setColor(new Color(0.0f, 0.0f, 0.7f, 0.1f)); g.fillRect(0, 0, getWidth(), getHeight()); } } } private static class Template { private Point2D control1; private Point2D control2; private Image image; public Template(Point2D control1, Point2D control2) { this.control1 = control1; this.control2 = control2; } public Point2D getControl1() { return control1; } public Point2D getControl2() { return control2; } public Image getImage() { if (image == null) { NumberFormat formatter = getNumberFormatter(); String name = ""; name += formatter.format(control1.getX()) + '-' + formatter.format(control1.getY()); name += '-'; name += formatter.format(control2.getX()) + '-' + formatter.format(control2.getY()); try { image = ImageIO.read(getClass().getResourceAsStream("images/templates/" + name + ".png")); } catch (IOException e) { } } return image; } } }