Java 如何让缓冲图像类显示在GUI中?
我有一个程序,使用定时器切换图像来制作动画。当程序在其最后一个图像上时,我使用一个类来创建该图像的缓冲图像,并在其上添加文本。当显示动画的最后一个图像时,我想将显示的图像更改为缓冲图像。我不能让它工作。代码按原样播放,就好像粗体部分不存在一样。如果我删除上面的行,它会显示上面有文本的图像,而不会显示其他内容。我应该对代码进行哪些编辑以修复此问题 制作动画的类Java 如何让缓冲图像类显示在GUI中?,java,graphics,timer,bufferedimage,Java,Graphics,Timer,Bufferedimage,我有一个程序,使用定时器切换图像来制作动画。当程序在其最后一个图像上时,我使用一个类来创建该图像的缓冲图像,并在其上添加文本。当显示动画的最后一个图像时,我想将显示的图像更改为缓冲图像。我不能让它工作。代码按原样播放,就好像粗体部分不存在一样。如果我删除上面的行,它会显示上面有文本的图像,而不会显示其他内容。我应该对代码进行哪些编辑以修复此问题 制作动画的类 **import java.awt.event.*; import java.awt.Graphics; import java.
**import java.awt.event.*;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Font;
import java.awt.image.*;
import java.io.*;
import java.io.File;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.net.URL;
import javax.swing.*;
import javax.swing.*;
import javax.imageio.ImageIO;
/**
* Write a description of class Reveal here.
*
* @author (your name)
* @version (a version number or a date)
*/
public class Reveal extends JPanel
{
private JPanel panel = new JPanel(); //a panel to house the label
private JLabel label = new JLabel(); //a label to house the image
private String[] image = {"Jack in the Box 1.png","Jack in the Box 2.png","Jack in the Box 3.png","Jack in the Box 4.png","Jack in the Box 5.png","Jack in the Box 6.png","Jack in the Box 7.png"}; //an array to hold the frames of the animation
private ImageIcon[] icon = new ImageIcon[7]; //an array of icons to be the images
private JFrame f;
private TextOverlay TO;
private Timer timer;
private Timer timer2;
int x = 0;
int y = 4;
int counter = 0;
/**
* Constructor for objects of class Reveal
*/
public Reveal(String name, int number)
{
TO = new TextOverlay("Jack in the Box 7.png", name, number);
for (int h = 0; h < 7; h++){
icon[h] = new ImageIcon(image[h]);
icon[h].getImage();
}
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
//Sets the size of the window
f.setSize(800,850);
panel = new JPanel();
label = new JLabel();
label.setIcon( icon[x] );
panel.add(label);
setVisible(true);
f.add(panel);
display(name, number);
**f.add(TO);**
}
public void display(String name, int number){
timer = new Timer(150, new ActionListener(){
public void actionPerformed(ActionEvent e) {
if (counter > 27){
timer.stop();
timer2.start(); //starts the second half of the animation
}else{
if (x != 3){
x++;
}else{
x = 0;
}
label.setIcon( icon[x] );
counter++;
} //ends if-else
} //ends action method
}); //ends timer
timer2 = new Timer(250, new ActionListener(){
public void actionPerformed(ActionEvent e){
if (y > 6) {
timer2.stop();
}else{
label.setIcon( icon[y] );
y++;
} //ends if-else
} //ends action method
}); //ends timer2
timer.start();
}
}
**
}因此,您似乎对Swing的工作原理有误解,您可能会找到一些帮助 基本上,当您
启动计时器时,它不会在这一点阻塞,直到计时器结束(即使它停止了,也不会按您希望的方式工作)。相反,将创建一个新线程,并在指定的时间段后,向事件调度线程发出请求,以执行提供的Runnable
这意味着当你做一些像
f.add(panel);
display(name, number);
f.add(TO);
实际上,您正在将添加到JLabel
的组件上(因为框架使用的是边框布局
,中间位置是默认位置
相反,在第二个计时器完成时,您需要删除标签并将添加到
组件
timer2 = new Timer(250, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (y > 6) {
timer2.stop();
Container parent = label.getParent();
parent.remove(label);
parent.add(TO);
parent.revalidate();
} else {
label.setIcon(icon[y]);
y++;
} //ends if-else
} //ends action method
}); //ends timer2
可运行的示例。。。
导入java.awt.event.*;
导入java.awt.Graphics;
导入java.awt.Color;
导入java.awt.Font;
导入java.awt.*;
导入java.awt.image.buffereImage;
导入javax.swing.*;
导入javax.swing.border.LineBorder;
公共类JPanel{
公共静态void main(字符串[]args){
invokeLater(新的Runnable(){
@凌驾
公开募捐{
试一试{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}catch(ClassNotFoundException |实例化Exception | IllegalacessException |不支持ookandfeelException ex){
例如printStackTrace();
}
新揭示(“测试”,5);
}
});
}
private JPanel panel=new JPanel();//用于放置标签的面板
private JLabel=new JLabel();//存放图像的标签
私有图像图标[]图标=新图像图标[7];//要作为图像的图标数组
私有jf框架;
私有文本覆盖到;
私人定时器;
私人定时器2;
int x=0;
int y=4;
int计数器=0;
/**
*类的对象的构造函数
*/
公共显示(字符串名称、整数){
TO=新文本覆盖(“框中的插孔7.png”,名称,编号);
对于(int h=0;h<7;h++){
图标[h]=新图像图标(生成图像(h));
图标[h].getImage();
}
JFrame f=新的JFrame();
f、 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f、 setVisible(真);
//设置窗口的大小
f、 设置大小(800850);
panel=newJPanel(newGridBagLayout());
label=新的JLabel();
label.setIcon(图标[x]);
label.setboorder(新线条边框(颜色为红色));
面板。添加(标签);
f、 添加(面板);
显示器(名称、编号);
//f.添加(至);
setVisible(真);
}
公共无效显示(字符串名称、整数){
计时器=新计时器(150,新ActionListener(){
已执行的公共无效操作(操作事件e){
如果(计数器>27){
timer.stop();
timer2.start();//开始动画的后半部分
}否则{
如果(x!=3){
x++;
}否则{
x=0;
}
label.setIcon(图标[x]);
计数器++;
}//否则结束
}//结束操作方法
});//结束计时器
timer2=新计时器(250,新ActionListener(){
已执行的公共无效操作(操作事件e){
如果(y>6){
timer2.stop();
容器父级=label.getParent();
移除(标签);
父。添加(到);
parent.revalidate();
}否则{
label.setIcon(图标[y]);
y++;
}//否则结束
}//结束操作方法
});//结束时间2
timer.start();
}
受保护的缓冲区映像生成映像(int h){
BuffereImage img=新的BuffereImage(100100,BuffereImage.TYPE_INT_ARGB);
Graphics2D g2d=img.createGraphics();
FontMetrics fm=g2d.getFontMetrics();
字符串文本=整数.toString(h);
intx=(100-fm.stringWidth(text))/2;
int y=((100-fm.getHeight())/2)+fm.getAscent();
g2d.setColor(Color.BLUE);
g2d.fillRect(0,0,100,100);
g2d.setColor(Color.BLACK);
g2d.抽绳(文本,x,y);
g2d.dispose();
返回img;
}
公共类TextOverlay扩展了JPanel{
私有缓冲图像;
私有字符串名称;
私有字符串文件;
私有整数;
公共文本覆盖(字符串f、字符串s、整数n){
name=s;
数字=n;
fileX=f;
image=makeImage(n);
图像=进程(图像、名称、编号);
}
@凌驾
受保护组件(图形g){
超级组件(g);
g、 drawImage(图像,0,0,this);
}
@凌驾
公共维度getPreferredSize(){
返回新维度(100100);
}
专用BuffereImage进程(BuffereImage旧、字符串名称、整数){
我
timer2 = new Timer(250, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (y > 6) {
timer2.stop();
Container parent = label.getParent();
parent.remove(label);
parent.add(TO);
parent.revalidate();
} else {
label.setIcon(icon[y]);
y++;
} //ends if-else
} //ends action method
}); //ends timer2
import java.awt.event.*;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Font;
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.LineBorder;
public class Reveal extends JPanel {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
new Reveal("Test", 5);
}
});
}
private JPanel panel = new JPanel(); //a panel to house the label
private JLabel label = new JLabel(); //a label to house the image
private ImageIcon[] icon = new ImageIcon[7]; //an array of icons to be the images
private JFrame f;
private TextOverlay TO;
private Timer timer;
private Timer timer2;
int x = 0;
int y = 4;
int counter = 0;
/**
* Constructor for objects of class Reveal
*/
public Reveal(String name, int number) {
TO = new TextOverlay("Jack in the Box 7.png", name, number);
for (int h = 0; h < 7; h++) {
icon[h] = new ImageIcon(makeImage(h));
icon[h].getImage();
}
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
//Sets the size of the window
f.setSize(800, 850);
panel = new JPanel(new GridBagLayout());
label = new JLabel();
label.setIcon(icon[x]);
label.setBorder(new LineBorder(Color.RED));
panel.add(label);
f.add(panel);
display(name, number);
// f.add(TO);
setVisible(true);
}
public void display(String name, int number) {
timer = new Timer(150, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (counter > 27) {
timer.stop();
timer2.start(); //starts the second half of the animation
} else {
if (x != 3) {
x++;
} else {
x = 0;
}
label.setIcon(icon[x]);
counter++;
} //ends if-else
} //ends action method
}); //ends timer
timer2 = new Timer(250, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (y > 6) {
timer2.stop();
Container parent = label.getParent();
parent.remove(label);
parent.add(TO);
parent.revalidate();
} else {
label.setIcon(icon[y]);
y++;
} //ends if-else
} //ends action method
}); //ends timer2
timer.start();
}
protected BufferedImage makeImage(int h) {
BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
FontMetrics fm = g2d.getFontMetrics();
String text = Integer.toString(h);
int x = (100 - fm.stringWidth(text)) / 2;
int y = ((100 - fm.getHeight()) / 2) + fm.getAscent();
g2d.setColor(Color.BLUE);
g2d.fillRect(0, 0, 100, 100);
g2d.setColor(Color.BLACK);
g2d.drawString(text, x, y);
g2d.dispose();
return img;
}
public class TextOverlay extends JPanel {
private BufferedImage image;
private String name;
private String fileX;
private int number;
public TextOverlay(String f, String s, int n) {
name = s;
number = n;
fileX = f;
image = makeImage(n);
image = process(image, name, number);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
private BufferedImage process(BufferedImage old, String name, int number) {
int w = old.getWidth();
int h = old.getHeight();
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.drawImage(old, 0, 0, w, h, this);
g2d.setPaint(Color.black);
g2d.setFont(new Font("Franklin Gothic Demi Cond", Font.PLAIN, 30));
String s1 = name;
String s2 = Integer.toString(number);;
FontMetrics fm = g2d.getFontMetrics();
g2d.drawString(s1, 40, 90);
g2d.drawString(s2, 40, 140);
g2d.dispose();
return img;
}
}
}
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.swing.*;
public class Reveal extends JPanel {
public static void main(String[] args) {
new Reveal();
}
public Reveal() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private IntAnimatable animatable;
private List<ImageIcon> icons = new ArrayList<>(25);
private JLabel label = new JLabel();
public TestPane() {
setLayout(new GridBagLayout());
IntRange range = new IntRange(0, 111);
animatable = new IntAnimatable(range, Duration.ofSeconds(4), Easement.SLOWOUT, new AnimatableListener<Integer>() {
@Override
public void animationChanged(Animatable<Integer> animator) {
int value = animator.getValue();
int index = value % 7;
ImageIcon icon = icons.get(index);
if (label.getIcon() != icon) {
label.setIcon(icon);
}
}
}, new AnimatableLifeCycleAdapter<Integer>() {
@Override
public void animationCompleted(Animatable<Integer> animator) {
BufferedImage img = makeImage(3);
writeTextOverImage("Lucky number", img);
ImageIcon luckNumber = new ImageIcon(img);
label.setIcon(luckNumber);
}
});
for (int index = 0; index < 7; index++) {
icons.add(new ImageIcon(makeImage(index)));
}
Collections.shuffle(icons);
add(label);
Animator.INSTANCE.add(animatable);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
protected void writeTextOverImage(String text, BufferedImage img) {
Graphics2D g2d = img.createGraphics();
Font font = g2d.getFont();
font = font.deriveFont(Font.BOLD, font.getSize2D() + 2);
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
int width = img.getWidth();
int height = img.getWidth();
int x = (width - fm.stringWidth(text)) / 2;
int y = fm.getAscent();
g2d.setColor(Color.YELLOW);
g2d.drawString(text, x, y);
g2d.dispose();
}
protected BufferedImage makeImage(int h) {
BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
FontMetrics fm = g2d.getFontMetrics();
String text = Integer.toString(h);
int x = (100 - fm.stringWidth(text)) / 2;
int y = ((100 - fm.getHeight()) / 2) + fm.getAscent();
g2d.setColor(Color.BLUE);
g2d.fillRect(0, 0, 100, 100);
g2d.setColor(Color.WHITE);
g2d.drawString(text, x, y);
g2d.dispose();
return img;
}
/**** Range ****/
/*
A lot of animation is done from one point to another, this just
provides a self contained concept of a range which can be used to
calculate the value based on the current progression over time
*/
public abstract class Range<T> {
private T from;
private T to;
public Range(T from, T to) {
this.from = from;
this.to = to;
}
public T getFrom() {
return from;
}
public T getTo() {
return to;
}
@Override
public String toString() {
return "From " + getFrom() + " to " + getTo();
}
public abstract T valueAt(double progress);
}
public class IntRange extends Range<Integer> {
public IntRange(Integer from, Integer to) {
super(from, to);
}
public Integer getDistance() {
return getTo() - getFrom();
}
@Override
public Integer valueAt(double progress) {
int distance = getDistance();
int value = (int) Math.round((double) distance * progress);
value += getFrom();
return value;
}
}
/**** Animatable ****/
/*
The core concept of something that is animatable. This basic wraps up the
logic for calculating the progression of the animation over a period of time
and then use that to calculate the value of the range and then the observers
are notified so they can do stuff
*/
public class IntAnimatable extends AbstractAnimatableRange<Integer> {
public IntAnimatable(IntRange animationRange, Duration duration, Easement easement, AnimatableListener<Integer> listener, AnimatableLifeCycleListener<Integer> lifeCycleListener) {
super(animationRange, duration, easement, listener, lifeCycleListener);
}
}
public interface AnimatableListener<T> {
public void animationChanged(Animatable<T> animator);
}
public interface AnimatableLifeCycleListener<T> {
public void animationStopped(Animatable<T> animator);
public void animationCompleted(Animatable<T> animator);
public void animationStarted(Animatable<T> animator);
public void animationPaused(Animatable<T> animator);
}
public interface Animatable<T> {
public T getValue();
public void tick();
public Duration getDuration();
public Easement getEasement();
// Wondering if these should be part of a secondary interface
// Provide a "self managed" unit of work
public void start();
public void stop();
public void pause();
}
public class AnimatableLifeCycleAdapter<T> implements AnimatableLifeCycleListener<T> {
@Override
public void animationStopped(Animatable<T> animator) {
}
@Override
public void animationCompleted(Animatable<T> animator) {
}
@Override
public void animationStarted(Animatable<T> animator) {
}
@Override
public void animationPaused(Animatable<T> animator) {
}
}
public abstract class AbstractAnimatable<T> implements Animatable<T> {
private LocalDateTime startTime;
private Duration duration = Duration.ofSeconds(5);
private AnimatableListener<T> animatableListener;
private AnimatableLifeCycleListener<T> lifeCycleListener;
private Easement easement;
private double rawOffset;
public AbstractAnimatable(Duration duration, AnimatableListener<T> listener) {
this.animatableListener = listener;
this.duration = duration;
}
public AbstractAnimatable(Duration duration, AnimatableListener<T> listener, AnimatableLifeCycleListener<T> lifeCycleListener) {
this(duration, listener);
this.lifeCycleListener = lifeCycleListener;
}
public AbstractAnimatable(Duration duration, Easement easement, AnimatableListener<T> listener) {
this(duration, listener);
this.easement = easement;
}
public AbstractAnimatable(Duration duration, Easement easement, AnimatableListener<T> listener, AnimatableLifeCycleListener<T> lifeCycleListener) {
this(duration, easement, listener);
this.lifeCycleListener = lifeCycleListener;
}
public void setEasement(Easement easement) {
this.easement = easement;
}
@Override
public Easement getEasement() {
return easement;
}
public Duration getDuration() {
return duration;
}
protected void setDuration(Duration duration) {
this.duration = duration;
}
public double getCurrentProgress(double rawProgress) {
Easement easement = getEasement();
double progress = Math.min(1.0, Math.max(0.0, getRawProgress()));
if (easement != null) {
progress = easement.interpolate(progress);
}
return Math.min(1.0, Math.max(0.0, progress));
}
public double getRawProgress() {
if (startTime == null) {
return 0.0;
}
Duration duration = getDuration();
Duration runningTime = Duration.between(startTime, LocalDateTime.now());
double progress = rawOffset + (runningTime.toMillis() / (double) duration.toMillis());
return Math.min(1.0, Math.max(0.0, progress));
}
@Override
public void tick() {
if (startTime == null) {
startTime = LocalDateTime.now();
fireAnimationStarted();
}
double rawProgress = getRawProgress();
double progress = getCurrentProgress(rawProgress);
if (rawProgress >= 1.0) {
progress = 1.0;
}
tick(progress);
fireAnimationChanged();
if (rawProgress >= 1.0) {
fireAnimationCompleted();
}
}
protected abstract void tick(double progress);
@Override
public void start() {
if (startTime != null) {
// Restart?
return;
}
Animator.INSTANCE.add(this);
}
@Override
public void stop() {
stopWithNotitifcation(true);
}
@Override
public void pause() {
rawOffset += getRawProgress();
stopWithNotitifcation(false);
double remainingProgress = 1.0 - rawOffset;
Duration remainingTime = getDuration().minusMillis((long) remainingProgress);
setDuration(remainingTime);
lifeCycleListener.animationStopped(this);
}
protected void fireAnimationChanged() {
if (animatableListener == null) {
return;
}
animatableListener.animationChanged(this);
}
protected void fireAnimationCompleted() {
stopWithNotitifcation(false);
if (lifeCycleListener == null) {
return;
}
lifeCycleListener.animationCompleted(this);
}
protected void fireAnimationStarted() {
if (lifeCycleListener == null) {
return;
}
lifeCycleListener.animationStarted(this);
}
protected void fireAnimationPaused() {
if (lifeCycleListener == null) {
return;
}
lifeCycleListener.animationPaused(this);
}
protected void stopWithNotitifcation(boolean notify) {
Animator.INSTANCE.remove(this);
startTime = null;
if (notify) {
if (lifeCycleListener == null) {
return;
}
lifeCycleListener.animationStopped(this);
}
}
}
public abstract class AbstractAnimatableRange<T> extends AbstractAnimatable<T> {
private Range<T> range;
private T value;
public AbstractAnimatableRange(Range<T> range, Duration duration, AnimatableListener<T> listener) {
super(duration, listener);
this.range = range;
}
public AbstractAnimatableRange(Range<T> range, Duration duration, AnimatableListener<T> listener, AnimatableLifeCycleListener<T> lifeCycleListener) {
super(duration, listener, lifeCycleListener);
this.range = range;
}
public AbstractAnimatableRange(Range<T> range, Duration duration, Easement easement, AnimatableListener<T> listener) {
super(duration, easement, listener);
this.range = range;
}
public AbstractAnimatableRange(Range<T> range, Duration duration, Easement easement, AnimatableListener<T> listener, AnimatableLifeCycleListener<T> lifeCycleListener) {
super(duration, easement, listener, lifeCycleListener);
this.range = range;
}
protected void tick(double progress) {
setValue(range.valueAt(progress));
}
protected void setValue(T value) {
this.value = value;
}
@Override
public T getValue() {
return value;
}
}
/*
Easement, complicated, but fun
*/
public enum Easement {
SLOWINSLOWOUT(1d, 0d, 0d, 1d), FASTINSLOWOUT(0d, 0d, 1d, 1d), SLOWINFASTOUT(0d, 1d, 0d, 0d), SLOWIN(1d, 0d, 1d, 1d), SLOWOUT(0d, 0d, 0d, 1d);
private final double[] points;
private final List<PointUnit> normalisedCurve;
private Easement(double x1, double y1, double x2, double y2) {
points = new double[]{x1, y1, x2, y2};
final List<Double> baseLengths = new ArrayList<>();
double prevX = 0;
double prevY = 0;
double cumulativeLength = 0;
for (double t = 0; t <= 1; t += 0.01) {
Point2D xy = getXY(t);
double length = cumulativeLength + Math.sqrt((xy.getX() - prevX) * (xy.getX() - prevX) + (xy.getY() - prevY) * (xy.getY() - prevY));
baseLengths.add(length);
cumulativeLength = length;
prevX = xy.getX();
prevY = xy.getY();
}
normalisedCurve = new ArrayList<>(baseLengths.size());
int index = 0;
for (double t = 0; t <= 1; t += 0.01) {
double length = baseLengths.get(index++);
double normalLength = length / cumulativeLength;
normalisedCurve.add(new PointUnit(t, normalLength));
}
}
public double interpolate(double fraction) {
int low = 1;
int high = normalisedCurve.size() - 1;
int mid = 0;
while (low <= high) {
mid = (low + high) / 2;
if (fraction > normalisedCurve.get(mid).getPoint()) {
low = mid + 1;
} else if (mid > 0 && fraction < normalisedCurve.get(mid - 1).getPoint()) {
high = mid - 1;
} else {
break;
}
}
/*
* The answer lies between the "mid" item and its predecessor.
*/
final PointUnit prevItem = normalisedCurve.get(mid - 1);
final double prevFraction = prevItem.getPoint();
final double prevT = prevItem.getDistance();
final PointUnit item = normalisedCurve.get(mid);
final double proportion = (fraction - prevFraction) / (item.getPoint() - prevFraction);
final double interpolatedT = prevT + (proportion * (item.getDistance() - prevT));
return getY(interpolatedT);
}
protected Point2D getXY(double t) {
final double invT = 1 - t;
final double b1 = 3 * t * invT * invT;
final double b2 = 3 * t * t * invT;
final double b3 = t * t * t;
final Point2D xy = new Point2D.Double((b1 * points[0]) + (b2 * points[2]) + b3, (b1 * points[1]) + (b2 * points[3]) + b3);
return xy;
}
protected double getY(double t) {
final double invT = 1 - t;
final double b1 = 3 * t * invT * invT;
final double b2 = 3 * t * t * invT;
final double b3 = t * t * t;
return (b1 * points[2]) + (b2 * points[3]) + b3;
}
protected class PointUnit {
private final double distance;
private final double point;
public PointUnit(double distance, double point) {
this.distance = distance;
this.point = point;
}
public double getDistance() {
return distance;
}
public double getPoint() {
return point;
}
}
}
/**** Core Animation Engine ****/
public enum Animator {
INSTANCE;
private Timer timer;
private List<Animatable> properies;
private Animator() {
properies = new ArrayList<>(5);
timer = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
List<Animatable> copy = new ArrayList<>(properies);
Iterator<Animatable> it = copy.iterator();
while (it.hasNext()) {
Animatable ap = it.next();
ap.tick();
}
if (properies.isEmpty()) {
timer.stop();
}
}
});
}
public void add(Animatable ap) {
properies.add(ap);
timer.start();
}
protected void removeAll(List<Animatable> completed) {
properies.removeAll(completed);
}
public void remove(Animatable ap) {
properies.remove(ap);
if (properies.isEmpty()) {
timer.stop();
}
}
}
}