I have been making a sorting algorithm visualizer in Java using Java Swing and AWT and coded insertion sort to see if it would be able to work. The algorithm works in the sense that when this program is run you can shuffle and sort the algorithm using insertion short but you don’t actually see the algorithm in action. It just happens instantly and have been looking for ways to add some sort of delay but I can’t find any sources to help me.
I set it up with 4 java classes, the main which just init’s the window, the window class, array visualizer class, and the insertion sort class.
Window.java:
public class Window implements ActionListener { //Window width protected static int WINDOW_WIDTH = 1980; //Window height protected static int WINDOW_HEIGHT = 1080; private int delay = 100; //This will draw all our rectangles protected static ArrayVisualizer arrayVisualizer; //Make a new JFrame JFrame window = new JFrame(); JButton shuffleBut = new JButton("Shuffle"); JButton startSort = new JButton("Start Sorting"); public Window() { initWindow(); } private void addButtons() { //Shuffle Button shuffleBut.setBounds(100, 50, 100, 100); shuffleBut.setBackground(Color.white); shuffleBut.addActionListener(this); startSort.setBounds(500, 50, 100, 100);; startSort.setBackground(Color.white); startSort.addActionListener(taskPerformer); window.add(shuffleBut); window.add(startSort); } private void initWindow() { ImageIcon logo = new ImageIcon(); window = new JFrame(); window.setTitle("JAlgorithms"); window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); window.setSize(WINDOW_WIDTH, WINDOW_HEIGHT); window.setLocationRelativeTo(null); window.setResizable(false); addButtons(); arrayVisualizer = new ArrayVisualizer(); window.add(arrayVisualizer); arrayVisualizer.repaint(); //Will call paint component method window.pack(); window.setVisible(true); } @Override public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub if(e.getSource() == shuffleBut) { arrayVisualizer.shuffle(ArrayVisualizer.array); arrayVisualizer.repaint(); } } ActionListener taskPerformer = new ActionListener() { public void actionPerformed(ActionEvent event) { if(event.getSource() == startSort) { arrayVisualizer.sort(ArrayVisualizer.array); arrayVisualizer.repaint(); } } }; }
ArrayVisualizer.java:
public class ArrayVisualizer extends JPanel { protected static int[] array; private final int REC_WIDTH = 1; //1980 rectangles private final int NUMBER_OF_RECS = Window.WINDOW_WIDTH / REC_WIDTH; public ArrayVisualizer() { array = new int[NUMBER_OF_RECS]; generateRandom(array); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D graphics = (Graphics2D) g.create(); graphics.setColor(Color.orange); for(int i = 0; i < NUMBER_OF_RECS; i++) { int height = array[i] * 4; //Done for scaling int recX = i + (REC_WIDTH - 1) * i; //Read fillRect documentation int recY = Window.WINDOW_HEIGHT - height; //Read fillRect documentation graphics.fillRect(recX, recY, REC_WIDTH, height); } } //This will return the Dimension of the actual rectangles. i.e the rectangles will only exist when this exists, almost like a canvas in javascript @Override public Dimension getPreferredSize() { return new Dimension(Window.WINDOW_WIDTH, Window.WINDOW_HEIGHT); } //Creates a random unsorted array with numbers 1-200 protected void generateRandom(int[] array) { Random number = new Random(); for(int i = 0; i < NUMBER_OF_RECS; i++) { array[i] = number.nextInt(200); } } protected void shuffle(int[] array) { generateRandom(array); } protected void sort(int[] array) { InsertionSort.insertionSort(array); } }
InsertionSort.java:
public class InsertionSort { public static void insertionSort(int arr[]) { int n = arr.length; for (int i = 1; i < n; ++i) { int key = arr[i]; int j = i - 1; /* Move elements of arr[0..i-1], that are greater than key, to one position ahead of their current position */ while (j >= 0 && arr[j] > key) { arr[j + 1] = arr[j]; j = j - 1; } arr[j + 1] = key; } } }
UPDATE (Here is the modified code, I added selection sort as well and heavily modified the suggestion I checkmarked):
Main.java:
public class Main { public static void main(String[] args) { //Opens window Window window = new Window(); } }
Window.java:
import java.awt.Color; import java.awt.Image; import java.awt.*; import javax.swing.*; import java.awt.event.*; import java.awt.EventQueue; import javax.swing.ImageIcon; import java.awt.Graphics2D; import java.awt.Graphics; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.BorderFactory; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.Timer; public class Window implements ActionListener { //Window width protected static int WINDOW_WIDTH = 1440; //Window height protected static int WINDOW_HEIGHT = 1080; //This will draw all our rectangles protected static ArrayVisualizer arrayVisualizer; //Make a new JFrame JFrame window = new JFrame(); JButton shuffleBut = new JButton("Shuffle"); JButton startSort = new JButton("Start Sorting"); public Window() { initWindow(); } private void addButtons() { //Shuffle Button shuffleBut.setBounds(100, 50, 100, 100); shuffleBut.setBackground(Color.white); shuffleBut.addActionListener(this); startSort.setBounds(500, 50, 100, 100);; startSort.setBackground(Color.white); window.add(shuffleBut); window.add(startSort); } private void initWindow() { ImageIcon logo = new ImageIcon(); window = new JFrame(); window.setTitle("JAlgorithms"); window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); window.setSize(WINDOW_WIDTH, WINDOW_HEIGHT); window.setLocationRelativeTo(null); window.setResizable(false); addButtons(); startSort.addActionListener(taskPerformer); arrayVisualizer = new ArrayVisualizer(); window.add(arrayVisualizer); arrayVisualizer.repaint(); //Will call paint component method window.pack(); window.setVisible(true); } @Override public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub if(e.getSource() == shuffleBut) { arrayVisualizer.shuffle(ArrayVisualizer.array); arrayVisualizer.repaint(); } } ActionListener taskPerformer = new ActionListener() { @Override public void actionPerformed(ActionEvent event) { if(event.getSource() == startSort) { if(!timer.isRunning()) { //If timer is not running timer.setInitialDelay(0); //Set initial delay timer.start(); //Start the timer } } } }; ActionListener sortWithDelay = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if(sorted(ArrayVisualizer.array)) { //If it is sorted timer.stop(); //Stop the timer return; } else { arrayVisualizer.sort(ArrayVisualizer.array); //If it is not sorted continue the sort arrayVisualizer.repaint(); //Called after each swap } } }; private int delay = 10; //Milliseconds private Timer timer = new Timer(delay, sortWithDelay); private boolean sorted(int[] array) { for(int i = 0; i < array.length - 1; i++) { if(array[i] > array[i+1]) { return false; } } return true; } }
ArrayVisualizer.java:
import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.lang.reflect.Array; import javax.swing.*; import java.util.*; public class ArrayVisualizer extends JPanel { private static final long serialVersionUID = 1L; private InsertionSort insertionSort = new InsertionSort(); private SelectionSort selectionSort = new SelectionSort(); protected static int[] array; private final int REC_WIDTH = 1; //1980 rectangles private final int NUMBER_OF_RECS = Window.WINDOW_WIDTH / REC_WIDTH; private final int[] barColors; public ArrayVisualizer() { setBackground(Color.black); array = new int[NUMBER_OF_RECS]; barColors = new int[NUMBER_OF_RECS]; generateRandom(array); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D graphics = (Graphics2D) g.create(); graphics.setColor(Color.white); for(int i = 0; i < NUMBER_OF_RECS; i++) { int height = array[i] * 4; //Done for scaling int recX = i + (REC_WIDTH - 1) * i; //Read fillRect documentation int recY = Window.WINDOW_HEIGHT - height; //Read fillRect documentation graphics.fillRect(recX, recY, REC_WIDTH, height); } } //This will return the Dimension of the actual rectangles. i.e the rectangles will only exist when this exists, almost like a canvas in javascript @Override public Dimension getPreferredSize() { return new Dimension(Window.WINDOW_WIDTH, Window.WINDOW_HEIGHT); } //Creates a random unsorted array with numbers 1-200 protected void generateRandom(int[] array) { Random number = new Random(); for(int i = 0; i < NUMBER_OF_RECS; i++) { array[i] = number.nextInt(200); } } protected void shuffle(int[] array) { generateRandom(array); } protected void sort(int[] array) { selectionSort.sortWithDelay(array); } }
InsertionSort.java:
public class InsertionSort { private int i = 1; private int j = 0; public void sortWithDelay(int arr[]) { int n = arr.length; if(i < n) { int key = arr[i]; j = i - 1; /* Move elements of arr[0..i-1], that are greater than key, to one position ahead of their current position */ while(j >= 0 && arr[j] > key) { arr[j + 1] = arr[j]; j = j - 1; } arr[j + 1] = key; ++i; } } }
SelectionSort.java:
public class SelectionSort { private int i = 1; private int j = 0; public void sortWithDelay(int arr[]) { int n = arr.length - 1; if(i < n) { int min_index = i; for(int j = i + 1; j < n; j++) { if(arr[j] < arr[min_index]) { min_index = j; } } int temp = arr[min_index]; arr[min_index] = arr[i]; arr[i] = temp; ++i; } } }
Advertisement
Answer
Call an action with a swing timer every second that will return an intermediate array. Then paint this intermediate array. Repeat until the array is sorted.
To obtain an intermediate array, modify the InsertionSort from a static method to a class method that will store the variable i
so that we can resume sorting when called again.
public class Window implements ActionListener { //Window width protected static int WINDOW_WIDTH = 1980; //Window height protected static int WINDOW_HEIGHT = 1080; //This will draw all our rectangles protected static ArrayVisualizer arrayVisualizer; //Make a new JFrame JFrame window = new JFrame(); JButton shuffleBut = new JButton("Shuffle"); JButton startSort = new JButton("Start Sorting"); public Window() { initWindow(); } private void addButtons() { //Shuffle Button shuffleBut.setBounds(100, 50, 100, 100); shuffleBut.setBackground(Color.white); shuffleBut.addActionListener(this); startSort.setBounds(500, 50, 100, 100);; startSort.setBackground(Color.white); startSort.addActionListener(taskPerformer); window.add(shuffleBut); window.add(startSort); } private void initWindow() { ImageIcon logo = new ImageIcon(); window = new JFrame(); window.setTitle("JAlgorithms"); window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); window.setSize(WINDOW_WIDTH, WINDOW_HEIGHT); window.setLocationRelativeTo(null); window.setResizable(false); addButtons(); arrayVisualizer = new ArrayVisualizer(); window.add(arrayVisualizer); arrayVisualizer.repaint(); //Will call paint component method window.pack(); window.setVisible(true); } @Override public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub if(e.getSource() == shuffleBut) { arrayVisualizer.shuffle(ArrayVisualizer.array); arrayVisualizer.repaint(); } } private int delay = 1000; // delay is 1s private Timer timer = new Timer(delay, sortWithDelay); ActionListener taskPerformer = new ActionListener() { @Override public void actionPerformed(ActionEvent event) { if(event.getSource() == startSort) { // Start the timer that shows a swap every second if(!timer.isRunning()) { timer.setInitialDelay(0); timer.start(); } } } }; ActionListener sortWithDelay = new ActionListener() { @Override public void actionPerformed(ActionEvent event) { if(isSorted(ArrayVisualizer.array)) { timer.stop(); return; } arrayVisualizer.sort(ArrayVisualizer.array); arrayVisualizer.repaint(); } }; private boolean isSorted(int[] arr) { for (int i = 0; i < a.length - 1; i++) { if (a[i] > a[i + 1]) { return false; } } return true; } }
public class ArrayVisualizer extends JPanel { private InsertionSort insertionSort = new InsertionSort(); protected static int[] array; private final int REC_WIDTH = 1; //1980 rectangles private final int NUMBER_OF_RECS = Window.WINDOW_WIDTH / REC_WIDTH; public ArrayVisualizer() { array = new int[NUMBER_OF_RECS]; generateRandom(array); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D graphics = (Graphics2D) g.create(); graphics.setColor(Color.orange); for(int i = 0; i < NUMBER_OF_RECS; i++) { int height = array[i] * 4; //Done for scaling int recX = i + (REC_WIDTH - 1) * i; //Read fillRect documentation int recY = Window.WINDOW_HEIGHT - height; //Read fillRect documentation graphics.fillRect(recX, recY, REC_WIDTH, height); } } //This will return the Dimension of the actual rectangles. i.e the rectangles will only exist when this exists, almost like a canvas in javascript @Override public Dimension getPreferredSize() { return new Dimension(Window.WINDOW_WIDTH, Window.WINDOW_HEIGHT); } //Creates a random unsorted array with numbers 1-200 protected void generateRandom(int[] array) { Random number = new Random(); for(int i = 0; i < NUMBER_OF_RECS; i++) { array[i] = number.nextInt(200); } } protected void shuffle(int[] array) { generateRandom(array); } protected void sort(int[] array) { insertionSort.sortWithDelay(array); } }
public class InsertionSort { private int i = 1; public void sortWithDelay(int arr[]) { int n = arr.length; if(i < n) { int key = arr[i]; int j = i - 1; /* Move elements of arr[0..i-1], that are greater than key, to one position ahead of their current position */ while(j >= 0 && arr[j] > key) { arr[j + 1] = arr[j]; j = j - 1; } arr[j + 1] = key; ++i; } } }