package source;
import java.util.ArrayList;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.util.Duration;
/**
* 汉诺塔动画演示: 面板
*
@Author ljh, created on 2019-06-05
*
*/
public class HanoiPane extends Pane {
int count_disk =5;
int count_move=0;
int interval = 2000;//移动盘子的间隔时间
Rod rod_01 = new Rod(), rod_02= new Rod(), rod_03= new Rod();
int height_disk = 20;//盘子的高度
int width_min_disk = 50;//最小盘子的直径(宽度)
int width_max_disk;//最大盘子的直径(宽度)
int diff_two_disk = 10;//盘子的半径(宽度的一半)差
int x0 = 30, y0 = 30;
Label label;
public HanoiPane(){
init();
draw();
}
public void init(){
width_max_disk = width_min_disk + 2*diff_two_disk*count_disk;
rod_01.seBtmPoint(new Point(x0+width_max_disk/2,y0+height_disk*(count_disk+1)));
rod_01.setTopPoint(new Point(rod_01.getBtmPoint().x,y0));
rod_02.seBtmPoint(new Point(rod_01.getBtmPoint().x+width_max_disk,rod_01.getBtmPoint().y));
rod_02.setTopPoint(new Point(rod_02.getBtmPoint().x,y0));
rod_03.seBtmPoint(new Point(rod_02.getBtmPoint().x+width_max_disk,rod_01.getBtmPoint().y));
rod_03.setTopPoint(new Point(rod_03.getBtmPoint().x,y0));
//setMaxSize(x0+width_max_disk*3+x0,p_btm_1.y);
//setMinSize(x0+width_max_disk*3+x0,p_btm_1.y);
//-fx-border-color:设置边框颜色;-fx-border-width设置边框宽度;-fx-background-color:设置背景颜色
setStyle("-fx-border-color:grey;-fx-background-color:lightgrey;-fx-border-width:5");
}
public void draw(){
//标签用于显示盘子移动的次数
label = new Label("移动次数:");
label.setLayoutX(10);
label.setLayoutY(10);
getChildren().add(label);
//画3根柱子
Line rod_line_1 = new Line(rod_01.getTopPoint().x, rod_01.getTopPoint().y, rod_01.getBtmPoint().x, rod_01.getBtmPoint().y);
Line rod_line_2 = new Line(rod_02.getTopPoint().x, rod_02.getTopPoint().y, rod_02.getBtmPoint().x, rod_02.getBtmPoint().y);
Line rod_line_3 = new Line(rod_03.getTopPoint().x, rod_03.getTopPoint().y, rod_03.getBtmPoint().x, rod_03.getBtmPoint().y);
getChildren().add(rod_line_1);
getChildren().add(rod_line_2);
getChildren().add(rod_line_3);
//画N个盘子
for(int n=0; n< count_disk; n++){
Rectangle rect = new Rectangle(rod_01.getTopPoint().x-(width_max_disk/2 - diff_two_disk*n),rod_01.getBtmPoint().y-height_disk-height_disk*n,
width_max_disk-2*diff_two_disk*n,height_disk);
rect.setFill(Color.GRAY);
rect.setStroke(Color.BLACK);
getChildren().add(rect);
rod_01.addDisk(rect);
}
}
public void start(){
new Thread(()-> //启动新的线程来移动盘子
{
move(count_disk,rod_01,rod_02, rod_03);
}
).start();
System.out.println(interval);
}
/**
* 递归:汉诺塔
*
@param n
* @param rod_start
* @param rod_end
* @param rod_temp
*/
public void move(int n, Rod rod_start, Rod rod_end, Rod rod_temp){
//todo:应用递归实现汉诺塔移动盘子
if(n == 1){
moveOneDisk(rod_start,rod_end);
}else {
move(n -1, rod_start, rod_temp, rod_end);
moveOneDisk(rod_start,rod_end);
move(n -1, rod_temp, rod_end, rod_start);
}
}
public void moveOneDisk(Rod rod_start, Rod rod_end){
Rectangle rect = rod_start.fetchDisk();
rod_end.addDisk(rect);
//rect.setFill(Color.RED);
count_move++;
System.out.println("移动次数:"+ count_move);
//由于label的操作不在javafx application的主线程中,所以要用Platform.runLater来控制,否则出错
Platform.runLater(()->
label.setText("移动次数:"+ count_move));
Timeline timeline = new Timeline();
timeline.getKeyFrames().addAll(
new KeyFrame( new Duration(0), // 时间0开如
new KeyValue(rect.xProperty(), rect.getX()),//原来的左上角坐标位置
new KeyValue(rect.yProperty(), rect.getY())
),
new KeyFrame(new Duration(interval/3), //总移动时间的第1/3,将盘子从原柱子中的移动到原柱子的顶部位置
new KeyValue(rect.xProperty(),rect.getX()),
new KeyValue(rect.yProperty(),y0)
),
new KeyFrame(new Duration(interval*2/3), //总移动时间的第2/3,将盘子从原柱子的顶部位置移动到目的柱子的顶部位置
new KeyValue(rect.xProperty(),rod_end.getTopPoint().x-rect.getWidth()/2),
new KeyValue(rect.yProperty(),y0)
),
new KeyFrame(new Duration(interval), //在总移动时间时刻,将盘子目的柱子的顶部位置移动到柱子中的位置
new KeyValue(rect.xProperty(),rod_end.getTopPoint().x-rect.getWidth()/2),
new KeyValue(rect.yProperty(),rod_end.getBtmPoint().y-rod_end.getCount()*height_disk)
)
);
timeline.play();
try {
Thread.sleep(interval+100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void movePause(){
Timeline timeline = new Timeline();
try {
timeline.pause();
Thread.sleep(interval+100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* 柱子类,用于存放柱子的相关信息,包括柱子以面板中的坐标,以及存放的盘子
* @author ljh
*
*/
class Rod {
ArrayList<Rectangle> list= new ArrayList<Rectangle>();//用于保存盘子
Point topPoint, btmPoint;
public Rectangle fetchDisk(){
Rectangle rect = list.get(list.size()-1);
list.remove(rect);
return rect ;
}
public void addDisk(Rectangle rect){
list.add(rect);
}
public int getCount(){
return list.size();
}
public void setTopPoint(Point p){
this.topPoint= p;
}
public void seBtmPoint(Point p){
this.btmPoint= p;
}
public Point getTopPoint(){
return topPoint;
}
public Point getBtmPoint(){
return btmPoint;
}
}