Reputation: 1165
For my algorithms class, our assignment is to perform mergesort on an array and show what is happening as an animation. I have the code (in theory) working, but when I call repaint() many times rapidly (to animate the array) they are getting ignored. No animation is being shown, just the final array. There should be one '-' after every ' * ' in the console but that is not the case, there are many ' * ' (as there should be) but only one '-'. The ' * ' denotes when the repaint method should be called, and the '-' denotes when it actually is called.
package a2;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class GraphicalSort extends JFrame implements ActionListener {
int[] data = new int[200];
int[] helper = new int[200];
JPanel panel = new JPanel(); //Panel to hold graphical display of array
JPanel buttonsPanel = new JPanel();
JButton mButton = new JButton("Mergesort");
JButton sButton = new JButton("Scramble");
//Constants to scale the width and height
int barWidth = 8;
int barHeight = 1;
public GraphicalSort() {
setLayout(new BorderLayout());
mButton.addActionListener(this);
sButton.addActionListener(this);
buttonsPanel.add(sButton);
buttonsPanel.add(mButton);
for (int i = 0; i < data.length; i++) {
data[i] = (int) (500 * Math.random() + 1);
helper[i] = data[i];
}
setSize(barWidth * data.length, barHeight * 500 + buttonsPanel.getHeight());
panel = new ArrayPanel();
add(buttonsPanel, BorderLayout.NORTH);
add(panel, BorderLayout.CENTER);
repaint();
validate();
}
public static void main(String[] args) {
GraphicalSort gs = new GraphicalSort();
gs.setTitle("Graphical Sort");
gs.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gs.setLocationRelativeTo(null);
gs.setResizable(false);
gs.setVisible(true);
}
@SuppressWarnings("serial")
class ArrayPanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
System.out.println("-"); //when repaint is actually called
int xPos = 0;
for (int i = 0; i < data.length; i++) {
g.fillRect(xPos, (barHeight * 500) - (barHeight * data[i]), barWidth, barHeight * data[i]);
xPos += barWidth;
}
}
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == mButton) {
mergesort(0, data.length - 1);
} else if (e.getSource() == sButton) {
Random r = new Random();
for (int i = 0; i < data.length; i++) {
int index = r.nextInt(data.length);
int temp = data[i];
data[i] = data[index];
data[index] = temp;
panel.repaint();
}
}
}
private void mergesort(int low, int high) {
// Check if low is smaller then high, if not then the array is sorted
if (low < high) {
// Get the index of the element which is in the middle
int middle = (low + high) / 2;
// Sort the left side of the array
mergesort(low, middle);
// Sort the right side of the array
mergesort(middle + 1, high);
// Combine them both
merge(low, middle, high);
System.out.println("*"); //When the repaint should be called
panel.repaint();
}
}
private void merge(int low, int middle, int high) {
// Copy both parts into the helper array
for (int i = low; i <= high; i++) {
helper[i] = data[i];
}
int i = low;
int j = middle + 1;
int k = low;
// Copy the smallest values from either the left or the right side back
// to the original array
while (i <= middle && j <= high) {
if (helper[i] <= helper[j]) {
data[k] = helper[i];
i++;
} else {
data[k] = helper[j];
j++;
}
k++;
}
// Copy the rest of the left side of the array into the target array
while (i <= middle) {
data[k] = helper[i];
k++;
i++;
}
}
}
Upvotes: 1
Views: 934
Reputation: 7706
To see animation during "scramble", you can replace your action preformed method with the following. Optionally add code to limit scrambling to one scramble at a time.
public void actionPerformed(final ActionEvent e) {
if (e.getSource() == mButton) {
mergesort(0, data.length - 1);
} else if (e.getSource() == sButton) {
new Thread(new Runnable() {
public void run() {
final Random r = new Random();
for (int i = 0; i < data.length; i++) {
try {Thread.sleep(10);}
catch (final InterruptedException e)
{ e.printStackTrace(); }
final int index = r.nextInt(data.length);
final int temp = data[i];
data[i] = data[index];
data[index] = temp;
panel.repaint();
}
}
}).start();
}
}
Upvotes: 0
Reputation: 285405
Your merge sort is happening quicker than you think, and the repaints are being called so close together that they are getting "stacked" and when this happens, yes, they can get ignored. Even if they aren't being ignored, again the speed of the code would result in no visible change to the GUI, and even if the process did take a lot of time, the GUI would be locked since the Swing event thread would be too busy to paint.
The solution is to slow down your code, but not with Thread.sleep(...)
which again would tie up the Swing event thread if not called in a background thread, but rather with a Swing Timer. This would allow the code to progress in a slower step-wise fashion, but without tying up the Swing event thread.
Upvotes: 1