Tsp遍历路径可视化【javaFx实现--详细注释】

代码

package com.dam.heuristic.util.paint;

import com.dam.heuristic.ils.test.IlsApi;
import com.dam.heuristic.ts.test.TsApi;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;

import java.io.File;
import java.io.FileInputStream;

public class PaintTspResult extends Application {

    //存储每个城市对应的x,y坐标
    private double[][] cityPositionArr;
    //存储城市序列
    private int[] sequence;
    //当前的时间轴
    private Timeline nowTimeline;

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {

        调用tsp求解算法
        double[][] distanceMatrix = this.getDistanceMatrix();
        TsApi TSApi = new TsApi(20, 100000, 100, distanceMatrix);
        this.sequence = TSApi.solve();

        画图
        try {
            BorderPane root = new BorderPane();
            root.setStyle("-fx-padding: 20;");
            Scene scene = new Scene(root, 1600, 900);
            double canvasWid = 1500;
            double canvasHei = 800;
            //根据画布大小缩放坐标值
            this.fixPosition(canvasWid - 50, canvasHei - 50);

            //画布和画笔
            HBox canvasHbox = new HBox();
            Canvas canvas = new Canvas();
            canvas.setWidth(canvasWid);
            canvas.setHeight(canvasHei);
            canvasHbox.setPrefWidth(canvasWid);
            canvasHbox.getChildren().add(canvas);
            canvasHbox.setAlignment(Pos.CENTER);
            canvasHbox.setStyle("-fx-spacing: 20;" +
                    "-fx-background-color: #ecf1c3;");
            root.setTop(canvasHbox);
            GraphicsContext paintBrush = canvas.getGraphicsContext2D();

            //启动
            HBox hBox2 = new HBox();
            Button beginButton = new Button("启动 Tsp 路线可视化");
            hBox2.getChildren().add(beginButton);
            root.setBottom(hBox2);
            hBox2.setAlignment(Pos.CENTER);
            //启动仿真以及暂停仿真
            beginButton.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
                nowTimeline.play();
            });


            //创建扫描线连接动画
            nowTimeline = new Timeline();
            createAnimation(paintBrush, 0.1);
            drawAllCircle(paintBrush);

            primaryStage.setScene(scene);
            primaryStage.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 修正cityPositionArr的坐标,让画出来的点在画布内
     *
     * @param width
     * @param height
     */
    private void fixPosition(double width, double height) {
        double minX = Double.MAX_VALUE;
        double maxX = -Double.MAX_VALUE;
        double minY = Double.MAX_VALUE;
        double maxY = -Double.MAX_VALUE;

        for (int i = 0; i < this.cityPositionArr.length; i++) {
            minX = Math.min(minX, this.cityPositionArr[i][0]);
            maxX = Math.max(maxX, this.cityPositionArr[i][0]);
            minY = Math.min(minY, this.cityPositionArr[i][1]);
            maxY = Math.max(maxY, this.cityPositionArr[i][1]);
        }

        double multiple = Math.max((maxX - minX) / width, (maxY - minY) / height);

        for (int i = 0; i < this.cityPositionArr.length; i++) {
            this.cityPositionArr[i][0] = this.cityPositionArr[i][0] / multiple + 20;
            this.cityPositionArr[i][1] = this.cityPositionArr[i][1] / multiple + 20;
        }
    }

    /**
     * 用画笔在画布上画出所有的孔
     */
    private void drawAllCircle(GraphicsContext paintBrush) {
        paintBrush.setStroke(Color.BLACK);
        for (int i = 0; i < this.sequence.length; i++) {
            drawCircle(paintBrush, this.sequence[i]);
        }
    }

    /**
     * 用画笔在画布上画出一个孔
     */
    private void drawCircle(GraphicsContext paintBrush, int cityCode) {
        double x = this.cityPositionArr[cityCode][0];
        double y = this.cityPositionArr[cityCode][1];
        double radius = 2;
        // 圆的直径
        double diameter = radius * 2;
        paintBrush.strokeOval(x, y, diameter, diameter);
    }

    /**
     * 将原始的线转化成一条可以画出来的线
     */
    private void drawLine(GraphicsContext paintBrush, int index) {
        int nextCityIndex = index + 1;
        nextCityIndex = nextCityIndex >= this.sequence.length ? 0 : nextCityIndex;
//        System.out.println(this.sequence[index]+">>"+this.sequence[nextCityIndex]);
        double startX = this.cityPositionArr[this.sequence[index]][0];
        double startY = this.cityPositionArr[this.sequence[index]][1];
        double endX = this.cityPositionArr[this.sequence[nextCityIndex]][0];
        double endY = this.cityPositionArr[this.sequence[nextCityIndex]][1];
        paintBrush.setStroke(Color.RED);
        paintBrush.strokeLine(startX, startY, endX, endY);
    }

    /**
     * 创建动画
     */
    private void createAnimation(GraphicsContext paintBrush, double speed) {
        for (int i = 0; i < this.sequence.length; i++) {
            int finalI = i;
            KeyFrame keyFrame = new KeyFrame(Duration.seconds(finalI * speed), event -> drawLine(paintBrush, finalI));
            nowTimeline.getKeyFrames().add(keyFrame);
        }
    }

    /**
     * 读取数据
     *
     * @return
     * @throws Exception
     */
    public double[][] getDistanceMatrix() throws Exception {
        读取数据
        String data = read(new File("src/main/java/com/data/tsp/att48.txt"), "GBK");
        String[] cityDataArr = data.split("\n");
        //初始化数组
        //距离矩阵,可以直接获取任意两个编号城市的距离
        double[][] distanceMatrix = new double[cityDataArr.length][cityDataArr.length];
        this.cityPositionArr = new double[cityDataArr.length][2];
        for (int i = 0; i < cityDataArr.length; i++) {
            String[] city1Arr = cityDataArr[i].split(" ");
            this.cityPositionArr[i][0] = Double.valueOf(city1Arr[1]);
            this.cityPositionArr[i][1] = Double.valueOf(city1Arr[2]);
            int cityOne = Integer.valueOf(city1Arr[0]);
            for (int j = 0; j < i; j++) {
                String[] city2Arr = cityDataArr[j].split(" ");
                int cityTwo = Integer.valueOf(city2Arr[0]);
                if (cityOne == cityTwo) {
                    distanceMatrix[cityOne - 1][cityTwo - 1] = 0;
                } else {
                    distanceMatrix[cityOne - 1][cityTwo - 1] = getDistance(Double.valueOf(city1Arr[1]), Double.valueOf(city1Arr[2]), Double.valueOf(city2Arr[1]), Double.valueOf(city2Arr[2]));
                    //对称赋值
                    distanceMatrix[cityTwo - 1][cityOne - 1] = distanceMatrix[cityOne - 1][cityTwo - 1];
                }
            }
        }
        return distanceMatrix;
    }

    /**
     * 给定两个城市坐标,获取两个城市的直线距离
     *
     * @param x1
     * @param y1
     * @param x2
     * @param y2
     * @return
     */
    private double getDistance(double x1, double y1, double x2, double y2) {
        return Math.sqrt((Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2)) / 10);
    }

    private String read(File f, String charset) throws Exception {
        FileInputStream fstream = new FileInputStream(f);
        try {
            int fileSize = (int) f.length();
            if (fileSize > 1024 * 512) {
                throw new Exception("File too large to read! size=" + fileSize);
            }

            byte[] buffer = new byte[fileSize];
            fstream.read(buffer);
            return new String(buffer, charset);
        } finally {
            try {
                fstream.close();
            } catch (Exception e) {
            }
        }
    }
}

测试

在这里插入图片描述

未经过算法优化的序列

  因为csdn上传的图片的大小会被限制为5M,所以降了分辨率和帧率才能导出这个图,看起来有点卡 /(ㄒoㄒ)/~~

在这里插入图片描述
在这里插入图片描述

经过算法优化的序列
  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hello Dam

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值