@[toc]
代码
/**
* @Author:WSKH
* @ClassName:PlotUtil
* @ClassType:
* @Description:
* @Date:2022/6/6/13:13
* @Email:1187560563@qq.com
* @Blog:https://blog.csdn.net/weixin_51545953?type=blog
*/
public class PlotUtil extends javafx.application.Application {
// 可视化区域的宽度
int stageW = 1000;
// 可视化区域的高度
int stageH = 1000;
// 坐标偏移
int offsetX = 100;
int offsetY = 100;
// 主要函数
@Override
public void start(Stage primaryStage) throws Exception {
// 初始化画布和面板
AnchorPane pane = new AnchorPane();
Canvas canvas = initCanvas(stageW - offsetX * 2, stageH - offsetY * 2);
canvas.relocate(offsetX, offsetY);
pane.getChildren().add(canvas);
// 读取城市坐标文件
List<double[]> locationList = getLocationList();
// 用数组型禁忌搜索算法求解TSP问题
ArrayTS_TSP arrayTSModel = new ArrayTS_TSP(new ArrayList<>(locationList));
arrayTSModel.solve();
// 获取最佳路径
int[] bestPath = arrayTSModel.bestGh.clone();
// 按照屏幕尺寸等比例调整城市坐标
List<double[]> fitLocationList = fitLocation(locationList);
// 绘制城市
HashMap<Integer, Circle> map = new HashMap<>();
List<Circle> circleList = new ArrayList<>();
for (double[] position : fitLocationList) {
Circle circle = new Circle(position[0], position[1], 5);
pane.getChildren().add(circle);
circleList.add(circle);
}
// 添加播放按钮
Button button = new Button("播放");
final int[] pCnt = {0};
button.setOnAction(new EventHandler<ActionEvent>() {
// 按钮点击事件
@Override
public void handle(ActionEvent event) {
// 路径 动画
Timeline animation = new Timeline(new KeyFrame(Duration.millis(50), new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
if (pCnt[0] < bestPath.length - 1) {
int cur = bestPath[pCnt[0]];
int next = bestPath[pCnt[0] + 1];
double[] p1 = fitLocationList.get(cur);
double[] p2 = fitLocationList.get(next);
Path path = new Path();
path.getElements().add(new MoveTo(p1[0], p1[1]));
path.getElements().add(new LineTo(p2[0], p2[1]));
pane.getChildren().add(path);
pCnt[0]++;
}else if(pCnt[0] == bestPath.length - 1){
int cur = bestPath[pCnt[0]];
int next = bestPath[0];
double[] p1 = fitLocationList.get(cur);
double[] p2 = fitLocationList.get(next);
Path path = new Path();
path.getElements().add(new MoveTo(p1[0], p1[1]));
path.getElements().add(new LineTo(p2[0], p2[1]));
pane.getChildren().add(path);
pCnt[0]++;
}
}
}));
animation.setCycleCount(Timeline.INDEFINITE);
animation.play();
}
});
pane.getChildren().add(button);
// 一切就绪,准备展示
primaryStage.setTitle("TSP路径可视化");
primaryStage.setScene(new Scene(pane, stageW, stageH));
primaryStage.show();
}
private List<double[]> fitLocation(List<double[]> locationList) {
// 获取宽度和高度方向上的最大值
double maxX = locationList.get(0)[0];
double maxY = locationList.get(0)[1];
for (double[] position : locationList) {
maxX = Math.max(maxX, position[0]);
maxY = Math.max(maxY, position[1]);
}
// 计算缩放比例
double rateX = (stageW - 2 * offsetX) / maxX;
double rateY = (stageH - 2 * offsetY) / maxY;
// 按照比例自适应调整
List<double[]> fitLocationList = new ArrayList<>();
for (double[] position : locationList) {
fitLocationList.add(new double[]{position[0] * rateX + offsetX, position[1] * rateY + offsetY});
}
return fitLocationList;
}
// 初始化画布对象
private Canvas initCanvas(double l, double w) {
Canvas canvas = new Canvas(l, w);
GraphicsContext gc = canvas.getGraphicsContext2D();
// 边框
gc.setStroke(Color.BLACK);
gc.setLineWidth(2);
gc.strokeRect(0, 0, l, w);
// 填充
gc.setFill(new Color(127 / 255d, 255 / 255d, 170 / 255d, 1d));
gc.fillRect(0, 0, l, w);
return canvas;
}
private List<double[]> getLocationList() throws IOException {
FileInputStream fis = new FileInputStream("src/main/java/Algorithm/TSP问题/data/att48.txt");
List<double[]> locationList = new ArrayList<>();
int len = 0;
StringBuilder sb = new StringBuilder();
while ((len = fis.read()) != -1) {
sb.append((char) len);
}
fis.close();
String[] split = sb.toString().split("\n");
for (String s : split) {
String[] s1 = s.split(" ");
double[] doubles = new double[]{Double.parseDouble(s1[1]), Double.parseDouble(s1[2])};
locationList.add(doubles.clone());
}
return locationList;
}
public static void main(String[] args) {
launch(args);
}
}