JavaFX在带有动画的条形图中可视化算法

时间:2016-11-01 16:23:53

标签: java algorithm javafx

所以我真的坚持这个,我需要将代码底部的算法输出到JavaFX中,以显示算法是如何工作的,即冒泡排序将较小的条移动到对的前面我遇到的问题是我无法弄清楚如何让我的算法在JavaFX中正确显示。交换减半和随机工作,但我没有工作的部分代码或上部件,所以我仍然在黑暗中,我仍然是Java新手,JavaFX对我来说有点奇怪

import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import static javafx.scene.chart.XYChart.*;
import javafx.geometry.Insets;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;

public class hamrickP2 extends Application {
public static void main(String[] args) {
    hamrickP2.launch(args);
}

private static final int
    BAR_COUNT = 14,
    MAX_BAR_HEIGHT = 50;

private static final String
    COLOR_ACTIVE = "-fx-bar-fill: #f64",
    COLOR_INITIAL = "-fx-bar-fill: #888",
    COLOR_FINALIZED = "-fx-bar-fill: #3cf";

private static final int
    DELAY_MILLIS = 700;

@Override
public void start(Stage stage) {
    stage.setTitle("Sorting Animations");
    stage.setWidth(800);
    stage.setHeight(600);

    final BorderPane pane = new BorderPane();
    pane.setPadding(new Insets(10));

    final BarChart<String,Number> chart = new BarChart<>(new      CategoryAxis(), new NumberAxis(0, MAX_BAR_HEIGHT, 0));
    chart.setLegendVisible(false);
    chart.getYAxis().setTickLabelsVisible(false);
    chart.getYAxis().setOpacity(0);
    chart.getXAxis().setTickLabelsVisible(false);
    chart.getXAxis().setOpacity(0);
    chart.setHorizontalGridLinesVisible(false);
    chart.setVerticalGridLinesVisible(false);

    bars = new ArrayList<Data<String,Number>>();
    final Series<String,Number> data = new Series<>();
    chart.getData().add(data);
    for (int i = 0; i < BAR_COUNT; i++) {
        bars.add(new Data<>(Integer.toString(i+1), 1));
        data.getData().add(bars.get(i));
        paint(i, COLOR_INITIAL);
    }
    pane.setCenter(chart);

    inputs = new FlowPane();
    inputs.setHgap(5);
    inputs.setVgap(5);
    createButton("Randomize", () -> randomizeAll());
    createButton("Swap Halves", () -> swapHalves());
    createButton("Reverse ", () -> reverse()); 
    createButton("Selection Sort", () -> selectionSort());


    pane.setBottom(inputs);

    stage.setScene(new Scene(pane));
    stage.show();

    Platform.runLater(() -> randomizeAll());
}

    private ArrayList<Data<String,Number>> bars;
    private FlowPane inputs;

    private void createButton(String label, Runnable method) {
    final Button test = new Button(label);
    test.setOnAction(event -> new Thread(() -> {
        Platform.runLater(() -> inputs.setDisable(true));
        method.run();
        Platform.runLater(() -> inputs.setDisable(false));
    }).start());
    inputs.getChildren().add(test);
}



// CHART ACCESSORS AND MUTATORS

private void assign(int index, int value) {
    bars.get(index).setYValue(value);
}

private int retrieve(int index) {
    return (int) bars.get(index).getYValue();
}

// ANIMATION CONTROLS

private void paint(int index, final String style) {
    Platform.runLater(() -> {
        bars.get(index).getNode().setStyle(style);
    });
}

private void paintAll(final String style) {
    Platform.runLater(() -> {
        for (int i = 0; i < BAR_COUNT; i++) paint(i, style);
    });
}

private void delay() {
    try {
        Thread.sleep(DELAY_MILLIS);
    }
    catch (InterruptedException e) {
        e.printStackTrace();
    }
}

// ALGORITHMS FOR BUTTONS

private void randomizeAll() {
    Random random = new Random();
    for (int i = 0; i < BAR_COUNT; i++) {
        assign(i, random.nextInt(MAX_BAR_HEIGHT) + 1);
    }
}

private void swapHalves() {
    final int half = bars.size()/2;
    final int offset = bars.size() % 2;
    for (int i = 0; i < half; i++) {
        final int j = i + half + offset;

        paint(i, COLOR_ACTIVE);
        paint(j, COLOR_ACTIVE);

        int temp = retrieve(i);
        assign(i, retrieve(j));
        assign(j, temp);

        delay();

        paint(i, COLOR_FINALIZED);
        paint(j, COLOR_FINALIZED);
    }
    paintAll(COLOR_INITIAL);
}// Start of algorithms 

/**
 * Reverse algorithm 
 * @param array
 * @return
 */
public void reverse(){
    int array [] = new int [BAR_COUNT];
    for(int pos = 0; pos < array.length; pos++)
    {

        int generic = array[pos];
        array[pos] = array[array.length - 1 - pos];
        array[array.length -1 - pos] = generic;

        int temp = array[array.length -1 - pos];

        assign(array[pos] , retrieve(array[array.length - 1 - pos]));
        assign(temp , generic);

        delay();


        paint(pos, COLOR_FINALIZED);
        paint( temp, COLOR_FINALIZED);


    }
    paintAll(COLOR_INITIAL);

}



/**
 * Selection Sort algorithm
 * @param words
 */
public  void selectionSort(){
    int arr [] = new int [BAR_COUNT];

    for (int i = 0; i < arr.length - 1; i++)
    {
        paint(i, COLOR_ACTIVE);

        int index = i;
        for (int j = i + 1; j < arr.length; j++)
            if (arr[j] < arr[index]) 
                index = j;

        int smallerNumber = arr[index];  
        arr[index] = arr[i];
        arr[i] = smallerNumber;

        int temp = retrieve(i);
        assign(i, retrieve(i));
        assign(index, temp);

        delay();

        paint(i, COLOR_FINALIZED);
        paint(index, COLOR_FINALIZED);

    }
    paintAll(COLOR_INITIAL);
}
        /**
         * Bubble Sort algorithm
         * @param words
         */
        public  void bubbleSort(){
            int array [] = new int [BAR_COUNT];
            int temp;
            for(int i = 0; i <array.length; i++){

                for(int j = 1; j <array.length -i; j++){
                    if(array[j-1] > array[j]){
                        temp = array[j -1];
                        array[j-1] = array[j];
                        array[j] = temp;
                    }
                }
            }
        }

                    /**
             * Insertion Sort algorithm
             * @param array
             */
    public  void insertionSort (){

        int array [] = new int [BAR_COUNT];
        for (int i = 1; i < array.length; i++) {
            int temp = array[i];
            int j;
            for(j = i -1; j>= 0 && temp < array[j]; j--){
                array[j +1 ] = array[j];
            array[j + 1 ] =temp;
        }
            paintAll(COLOR_INITIAL);    
    }
}
}

1 个答案:

答案 0 :(得分:0)

我在代码中发现了一些问题。请考虑以下事项:

  1. bars设为ObservableList并将其用于您的系列。 Series实例将采用此列表并在内部使用它。通过这种方式,您可以观察列表并稍后修改列表,同时图表会自动更新。访问数据就像使用条形图作为列表或从条形图中提取数组一样简单。我做了两个bubblesort版本。一个访问栏作为列表(inplace),一个访问数组(不到位)。
  2. 向条形图添加新的Data对象后,您将能够访问节点以更改颜色。无需使用Platform.runlater。无论如何,Runlate被滥用,因为无法保证您等待的资源将在稍后的时间点出现。如果要对将在未知时间点出现的资源执行某些操作,请观察它是否为可观察值。使用addListener并注意更改。
  3. 请勿使用线程并在其中修改GUI中的数据。使用runlater来禁用按钮是可以的,但是你在线程中做了很多其他事情。此外,您的runlater的主体可能会稍后调用(O.o)。很久以后,你的Thread已经完成了。 Runlater是异步调用。
  4. 如果要在计划的时间范围内修改gui,请使用JavaFX动画类。 Timeline可能是您正在寻找的。 This tutorial引入了这个概念。
  5. 我从你的代码中做了一个例子。我删除了一些东西以使其更清晰:

    import javafx.application.Application;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.geometry.Insets;
    import javafx.scene.Node;
    import javafx.scene.Scene;
    import javafx.scene.chart.BarChart;
    import javafx.scene.chart.CategoryAxis;
    import javafx.scene.chart.NumberAxis;
    import javafx.scene.control.Button;
    import javafx.scene.layout.BorderPane;
    import javafx.scene.layout.FlowPane;
    import javafx.stage.Stage;
    
    import java.util.List;
    import java.util.Random;
    
    import static javafx.scene.chart.XYChart.Data;
    import static javafx.scene.chart.XYChart.Series;
    
    public class Main extends Application {
        public static void main(String[] args) {
            launch(args);
        }
    
        private static final int
            BAR_COUNT = 14,
            MAX_BAR_HEIGHT = 50;
    
        private static final String
            COLOR_ACTIVE = "-fx-bar-fill: #f64",
            COLOR_INITIAL = "-fx-bar-fill: #888",
            COLOR_FINALIZED = "-fx-bar-fill: #3cf";
    
        private static final int
            DELAY_MILLIS = 700;
    
    
        private ObservableList<Data<String, Number>> bars;
        private BarChart<String, Number> chart;
        private FlowPane inputs;
    
    
        @Override
        public void start(Stage stage) {
            stage.setTitle("Sorting Animations");
            stage.setWidth(800);
            stage.setHeight(600);
    
            final BorderPane pane = new BorderPane();
            pane.setPadding(new Insets(10));
    
            makeChart(pane);
            makeButtons(pane);
    
            stage.setScene(new Scene(pane));
            stage.show();
    
            randomizeAll();
        }
    
        private void makeChart(BorderPane pane) {
            chart = new BarChart<>(new CategoryAxis(), new NumberAxis(0, MAX_BAR_HEIGHT, 0));
            chart.setLegendVisible(false);
            chart.getYAxis().setTickLabelsVisible(false);
            chart.getYAxis().setOpacity(0);
            chart.getXAxis().setTickLabelsVisible(false);
            chart.getXAxis().setOpacity(0);
            chart.setHorizontalGridLinesVisible(false);
            chart.setVerticalGridLinesVisible(false);
    
            bars = FXCollections.observableArrayList();
            chart.getData().add(new Series<>(bars));
    
            for (int i = 0; i < BAR_COUNT; i++) {
                Data<String, Number> dataObject = new Data<>(Integer.toString(i + 1), 1);
                bars.add(dataObject); // node will be present after this
                addPainting(dataObject.getNode(), COLOR_INITIAL); // do this after bars.add
            }
            pane.setCenter(chart);
        }
    
    
        private void makeButtons(BorderPane pane) {
            inputs = new FlowPane();
            inputs.setHgap(5);
            inputs.setVgap(5);
            createButton("Randomize", () -> randomizeAll());
            createButton("Bubble Sort 1", () -> bubbleSort1());
            createButton("Bubble Sort 2", () -> bubbleSort2());
            pane.setBottom(inputs);
        }
    
        private void addPainting(Node newNode, String colorInitial) {
            if (newNode != null) {
                newNode.setStyle(colorInitial);
            }
        }
    
        private void createButton(String label, Runnable method) {
            final Button test = new Button(label);
            test.setOnAction(event -> method.run());
            inputs.getChildren().add(test);
        }
    
    
        private void randomizeAll() {
            Random random = new Random();
            for (Data<String, Number> bar : bars) {
                bar.setYValue(random.nextInt(MAX_BAR_HEIGHT) + 1);
            }
        }
    
    
        /**
         * Bubble Sort algorithm
         */
        private void bubbleSort1(){
            List<Data<String, Number>> list = bars;
    
            double temp;        
            for(int i = 0; i < list.size(); i++){
                for(int j = 1; j < list.size() - i; j++){
                    if (getValue(list, j - 1) > getValue(list, j)){
                        temp = getValue(list, j - 1);
                        list.get(j - 1).setYValue(list.get(j).getYValue());
                        list.get(j).setYValue(temp);
                    }
                }
            }
        }
    
        private double getValue(List<Data<String, Number>> list, int index) {
            return list.get(index).getYValue().doubleValue();
        }
    
    
        /**
         * Bubble Sort algorithm
         */
        private void bubbleSort2(){
            double[] array = bars.stream().mapToDouble(data -> data.getYValue().doubleValue()).toArray();
    
            double temp;
            for(int i = 0; i <array.length; i++){
                for(int j = 1; j <array.length -i; j++){
                    if(array[j-1] > array[j]){
                        temp = array[j -1];
                        array[j-1] = array[j];
                        array[j] = temp;
                    }
                }
            }
            for (int i = 0; i < array.length; i++) {
                bars.get(i).setYValue(array[i]);
            }
        }
    
    }
    

    更新1 如果您以可观察的方式实现了分配和检索工作,就像您在问题中已经完成的那样工作。

    private void assign(int index, int value) {
        bars.get(index).setYValue(value);
    }
    
    private int retrieve(int index) {
        return (int) bars.get(index).getYValue();
    }
    
    我以同样的方式做到了;例如看到最后一个for循环的主体。

    要为图表设置动画,您必须重写算法,以便每次调用只执行一步,而不是一次排序整个数组。 例如。在第一次交换后,对bubblesort的函数调用返回。

    最后一步是初始化一个TimeLine(Timer),每隔t秒调用一步的bubblesort算法。

    棘手的部分是如何记住算法的最后一次运行停止的位置以及如何继续运行。