diff --git a/AlgorithmVisualizer.java b/AlgorithmVisualizer.java index c25e82c..4ba00a6 100644 --- a/AlgorithmVisualizer.java +++ b/AlgorithmVisualizer.java @@ -1,5 +1,6 @@ -public class AlgorithmVisualizer { - public static void main(String[] args){ - new Menu(); - } -} \ No newline at end of file +/** AlgorithmVisualizer.java is responsibe for running the project */ +public class AlgorithmVisualizer { + public static void main(String[] args){ + new Main(); // referencing to the Main Class + } +} diff --git a/Main.java b/Main.java new file mode 100644 index 0000000..62b99d2 --- /dev/null +++ b/Main.java @@ -0,0 +1,81 @@ +/** + * Main.java includes: + * - Menus and menu items + * - Main frame + */ + +import java.awt.event.*; +import javax.swing.*; + +public class Main extends JFrame { + // Menubar + static JMenuBar menuBar; + + // JMenu + static JMenu mainMenu, helpDesk; + + // Menu items + static JMenuItem menuItem1, menuItem2, menuItem3, menuItem4; + + Main() { + // Create a menubar + menuBar = new JMenuBar(); + + // Create a menu + mainMenu = new JMenu("Menu"); + helpDesk = new JMenu("Help"); + + // Create a menu items + menuItem1 = new JMenuItem("Path Finding"); // Its for future vision + menuItem2 = new JMenuItem("Sorting Techniques"); // Done + menuItem3 = new JMenuItem("Puzzle Sorting"); // Its for future vision + menuItem4 = new JMenuItem("How it Works"); // About + + ListenerClass listener = new ListenerClass(); + + // Register listeners + menuItem1.addActionListener(listener); + menuItem2.addActionListener(listener); + menuItem3.addActionListener(listener); + + // Add menu items to menu + mainMenu.add(menuItem1); + mainMenu.add(menuItem2); + mainMenu.add(menuItem3); + helpDesk.add(menuItem4); + + // Add menu to menu bar + menuBar.add(mainMenu); + menuBar.add(helpDesk); + + // Add menubar to frame + setJMenuBar(menuBar); + + setTitle("Eightsoft"); + setSize(800, 800); + setLocation(300, 5); + setVisible(true); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } + + class ListenerClass implements ActionListener { + String value; + + // Project Main Logic (Moving Panels) + public void actionPerformed(ActionEvent e) { + if (e.getSource() == menuItem1) { + System.out.println(" Path Finding Algorith (We will add it in future for portfolio)"); + } + else if (e.getSource() == menuItem2) { + // Creating Object + new Sorting(); + System.out.println("Menu Item 2 choosed"); + } + else if (e.getSource() == menuItem2) { + System.out.println(" Picture pazzling algorith (We will add it in future for portfolio)"); + } + setVisible(false); // will close the previous window + } + } + +} // Menu diff --git a/Menu.java b/Menu.java deleted file mode 100644 index 016a429..0000000 --- a/Menu.java +++ /dev/null @@ -1,97 +0,0 @@ -import javax.swing.*; -import java.awt.*; -import java.awt.event.*; -import java.util.*; - -public class Menu extends JFrame { - // Creating Object - Sorting sort = new Sorting(); - - // Menubar - static JMenuBar menuBar; - - // JMenu - static JMenu mainMenu, helpDesk; - - // Menu items - static JMenuItem menuItem1, menuItem2, menuItem3, menuItem4; - - // A Panel - static JPanel pPanel1, pPanel2; - - Menu() { - - // Create Panel - pPanel1 = new JPanel(); - pPanel2 = new JPanel(); - - // Create a menubar - menuBar = new JMenuBar(); - - // Create a menu - mainMenu = new JMenu("Menu"); - helpDesk = new JMenu("Help"); - - // Create a menu items - menuItem1 = new JMenuItem("Path Finding"); - menuItem2 = new JMenuItem("Sorting Techniques"); - menuItem3 = new JMenuItem("Puzzle Sorting"); - menuItem4 = new JMenuItem("How it Works"); - - ListenerClass listener = new ListenerClass(); - - // Register listeners - menuItem1.addActionListener(listener); - menuItem2.addActionListener(listener); - menuItem3.addActionListener(listener); - - // Add menu items to menu - mainMenu.add(menuItem1); - mainMenu.add(menuItem2); - mainMenu.add(menuItem3); - helpDesk.add(menuItem4); - - // Add menu to menu bar - menuBar.add(mainMenu); - menuBar.add(helpDesk); - - // Add menubar to frame - setJMenuBar(menuBar); - - // Add Panels - pPanel1.setBackground(Color.CYAN); - pPanel2.setBackground(Color.YELLOW); - - // Add Panel - pPanel1.add(sort.p1Sorting); - pPanel2.add(sort.p2Sorting); - sort.p1Sorting.setVisible(false); - sort.p2Sorting.setVisible(false); - - // Add Panels to the panel - add(pPanel1, BorderLayout.WEST); - add(pPanel2, BorderLayout.CENTER); - - setTitle("EightSoft"); - setSize(700,500); - setLocation(200, 100); - setVisible(true); - setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - } - - class ListenerClass implements ActionListener { - - // Project Main Logic (Moving Panels) - public void actionPerformed(ActionEvent e) { - if (e.getSource() == menuItem1) { - sort.p1Sorting.setVisible(false); - sort.p2Sorting.setVisible(false); - } - else if (e.getSource() == menuItem2) { - sort.p1Sorting.setVisible(true); - sort.p2Sorting.setVisible(true); - } - } - } - -} // Menu \ No newline at end of file diff --git a/README.md b/README.md index 156fc52..48e6b07 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,40 @@ # Algorithm Visualizer in Java - -By @Alimov-8👨🏻‍💻 and @Rustam-Z👨🏼‍💻 -Updated: December 5, 2020 +Developed by [@Rustam-Z](https://github.com/Rustam-Z) and [@Alimov-8](https://github.com/Alimov-8) -### References: -- https://www.geeksforgeeks.org/java-swing-jmenubar/ +Demo video: https://youtu.be/huR1NSwUnAY + +Finish date: December 13, 2020 + +## Project Features +- Sorting Algorithms Visualizer `DONE` +- [Picture Puzzle Sorter](https://www.youtube.com/watch?v=Bc0sVtB_-ak) `TODO` +- [Pathfinding algorithm](https://clementmihailescu.github.io/Pathfinding-Visualizer/) `TODO` + +## First Prototype + -- https://stackoverflow.com/questions/31094904/jmenu-not-appearing-until-window-is-resized \ No newline at end of file +## How to run? +``` +javac AlgorithmVisualizer.java +java AlgorithmVisualizer +``` + +## Skills Gained +- Multi-Threading using Swing Worker +- Customizing Swing Components +- Working with Graphics 2D +- Different sorting algorithms + +## References: +- https://clementmihailescu.github.io/Sorting-Visualizer/ +- https://www.geeksforgeeks.org/java-swing-jmenubar/ +- https://stackoverflow.com/questions/31094904/jmenu-not-appearing-until-window-is-resized +- https://stackoverflow.com/questions/4716372/java-how-do-i-close-a-jframe-while-opening-another-one +- First prototype video: https://www.youtube.com/watch?v=RxjXC1SM1A4 +- https://stackoverflow.com/questions/4246351/creating-random-colour-in-java +- https://stackoverflow.com/questions/782265/how-do-i-use-swingworker-in-java +- https://docs.oracle.com/javase/tutorial/essential/concurrency/sleep.html +- https://stackoverflow.com/questions/1097366/java-swing-revalidate-vs-repaint +- Merge sort: https://www.baeldung.com/java-merge-sort +- http://www.java2s.com/Tutorials/Java/Swing/JSlider/Add_change_event_listener_to_a_JSlider_in_Java.htm diff --git a/Sorting.java b/Sorting.java index 446d6f5..7315385 100644 --- a/Sorting.java +++ b/Sorting.java @@ -1,51 +1,118 @@ -import javax.swing.*; -import java.awt.*; -import java.awt.event.*; -import java.util.*; - -public class Sorting extends JFrame { - // Values - ArrayList list=new ArrayList();//Creating arraylist - - // Sorting Buttons - JButton jbtRandomize, jbtReset, jbtBubble, jbtInsertion, jbtSelection, jbtStart; // Sorting Buttons - JPanel p1Sorting, p2Sorting; - - // Random Creator - Random rand = new Random(); - - // Progress Bar - JProgressBar jb1; - - Sorting(){ - // Buttons for sorting - jbtRandomize = new JButton("Randomize");//create button - jbtReset = new JButton("Reset");//create button - jbtBubble = new JButton("Bubble sort");//create button - jbtInsertion = new JButton("Insertion sort");//create button - jbtSelection = new JButton("Selection sort");//create button - jbtStart = new JButton("Start");//create button - jbtStart.setBackground(Color.GREEN); - - // Panel for buttons - p1Sorting = new JPanel(); - p1Sorting.setLayout(new GridLayout(6,1)); - - // Adding Buttons to the panel - p1Sorting.add(jbtRandomize); p1Sorting.add(jbtReset); p1Sorting.add(jbtSelection); - p1Sorting.add(jbtBubble); p1Sorting.add(jbtInsertion); p1Sorting.add(jbtStart); - - - jb1 = new JProgressBar(0,100); - jb1.setValue(rand.nextInt(100)); - jb1.setStringPainted(true); - - // Panel 2 - p2Sorting = new JPanel(); - p2Sorting.setLayout(new GridLayout(10,1)); - p2Sorting.add(jb1, BorderLayout.WEST); - - } - -} - +// Main GUI part of Sorting. + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; + +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +public class Sorting extends Main { + // Object of the SortingAlgorithm, which includes the sorting algorithms + SortingAlgorithm sortAlgo = new SortingAlgorithm(); + + // Panels: pPanel1 - option bar, pPanel2 - visualization bar + JPanel pPanel1, pPanel2; + + // Option buttons for choosing sorting techniques, speed, and size of array + // Will be added to pPanel1 + JButton jbtRandomize, jbtMerge, jbtBubble, jbtInsertion, jbtSelection, jbtStart; + + // Progress Bar + JProgressBar jb1; + + JSlider slider = new JSlider(0, 100, 2); + + public Sorting(){ + // Panel for options (bubble sort, insertion sort...) + pPanel1 = new JPanel(); + pPanel1.setLayout(new GridLayout(1, 7)); + pPanel1.setBackground(Color.CYAN); + + // Panel for visualization part + pPanel2 = new JPanel(); + pPanel2.setLayout(new BorderLayout()); + + // Buttons + jbtRandomize = new JButton("Randomize"); + jbtMerge = new JButton("Merge Sort"); + jbtBubble = new JButton("Bubble Sort"); + jbtInsertion = new JButton("Insertion Sort"); + jbtSelection = new JButton("Selection Sort"); + jbtStart = new JButton("Start"); + jbtStart.setBackground(Color.GRAY); + + // Slider + // slider.setPreferredSize(new Dimension(150, 30)); + // slider.setMajorTickSpacing(50); + // slider.setPaintTicks(false); + + // Adding elements to pPanel1 + pPanel1.add(jbtRandomize); + pPanel1.add(jbtMerge); pPanel1.add(jbtSelection); pPanel1.add(jbtBubble); pPanel1.add(jbtInsertion); + // pPanel1.add(jbtStart); + pPanel1.add(slider, BorderLayout.WEST); + + // Adding elements to pPanel2 + pPanel2.add(sortAlgo, BorderLayout.CENTER); + + // Register listener, event handling + ListenerClass listener = new ListenerClass(); + jbtRandomize.addActionListener(listener); + jbtMerge.addActionListener(listener); + jbtBubble.addActionListener(listener); + jbtInsertion.addActionListener(listener); + jbtSelection.addActionListener(listener); + jbtStart.addActionListener(listener); + + // Add Panels to the Main JFrame + add(pPanel1, BorderLayout.NORTH); + add(pPanel2, BorderLayout.CENTER); + + // Slider settings + slider.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent event) { + int value = slider.getValue(); + sortAlgo.setSIZE(value); + // Graphics g = new Graphics(); + // sortAlgo.initBarHeight(); + sortAlgo.BAR_WIDTH = (float)800 / sortAlgo.getSIZE(); // bar width + sortAlgo.repaint(); + System.out.println(value); + + } + }); + } + + class ListenerClass implements ActionListener { + // Handles the Button operations + + public void actionPerformed(ActionEvent e) { + + if (e.getSource() == jbtRandomize) { + sortAlgo.initShuffler(); + } + else if (e.getSource() == jbtMerge) { + sortAlgo.mergeSort(); // Merge sort algorithm + sortAlgo.initShuffler(); // shuffling + } + else if (e.getSource() == jbtBubble) { + sortAlgo.bubbleSort(); // Bubble sort algorithm + sortAlgo.initShuffler(); // shuffling + } + else if (e.getSource() == jbtInsertion) { + sortAlgo.insertionSort(); // Insertion algorithm + sortAlgo.initShuffler(); // shuffling + } + else if (e.getSource() == jbtSelection) { + sortAlgo.selectionSort(); // Insertion algorithm + sortAlgo.initShuffler(); // shuffling + } + else if (e.getSource() == jbtStart) { + System.out.println("jbtStart button clicked"); + } + } + } // ListenerClass + +} // Sorting diff --git a/SortingAlgorithm.java b/SortingAlgorithm.java new file mode 100644 index 0000000..0b106d2 --- /dev/null +++ b/SortingAlgorithm.java @@ -0,0 +1,221 @@ +// Implementation of Sorting algorithms + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; +import java.util.Random; + +import javax.swing.JPanel; +import javax.swing.SwingWorker; + +public class SortingAlgorithm extends JPanel { + private final int WIDTH = 800, HEIGHT = WIDTH * 9 / 16; + public int SIZE = 100; // the number if sorting elements + public float BAR_WIDTH = (float)WIDTH / SIZE; // bar width + private float[] bar_height = new float[SIZE]; // height of bars + private SwingWorker shuffler, sorter; + private int current_index, traversing_index; // needed for following colloring the items + + SortingAlgorithm() { + setBackground(Color.BLACK); + setPreferredSize(new Dimension(WIDTH, HEIGHT)); + initBarHeight(); // initialize the height of each bar + // initShuffler(); // shuffle each bar + } + + // setter for SIZE + public void setSIZE(int SIZE) { + this.SIZE = SIZE; + } + + // getter for SIZE + int getSIZE() { + return SIZE; + } + + // repaint() will automaticly call this function + // needed for coloring + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + + // Create randomizer + Random random = new Random(); + + // Drawing the rectangles + Graphics2D g2d = (Graphics2D)g; + g2d.setColor(Color.CYAN); + Rectangle2D.Float bar; + + for(int i = 0; i < getSIZE(); i++ ) { + // random colors + // final float hue = random.nextFloat(); + // final float saturation = 0.9f; //1.0 for brilliant, 0.0 for dull + // final float luminance = 1.0f; //1.0 for brighter, 0.0 for black + + // g2d.setColor(Color.getHSBColor(hue, saturation, luminance)); + bar = new Rectangle2D.Float(i * BAR_WIDTH, 0, BAR_WIDTH-1, bar_height[i]); + g2d.fill(bar); // g2d.draw(bar); + } + + // Color setter for the current object + g2d.setColor(Color.RED); + bar = new Rectangle2D.Float(current_index * BAR_WIDTH, 0, BAR_WIDTH, bar_height[current_index]); + g2d.fill(bar); + + // Color setter for the traversing object + g2d.setColor(Color.YELLOW); + bar = new Rectangle2D.Float(traversing_index * BAR_WIDTH, 0, BAR_WIDTH, bar_height[traversing_index]); + g2d.fill(bar); + } + + public void insertionSort() { + /*Insertion sort algorithm*/ + // Multithreading used for hadling the sorting + sorter = new SwingWorker<>() { + @Override + public Void doInBackground() throws InterruptedException { // function for calling multithreading + // Insertion sort algorithm starts + for(current_index = 1; current_index < getSIZE(); current_index++) { + traversing_index = current_index; + while(traversing_index > 0 && bar_height[traversing_index] < bar_height[traversing_index - 1]) { + swap(traversing_index, traversing_index - 1); + traversing_index--; + + Thread.sleep(10); // controls the speed + repaint(); // we need it because we ofter replace the contents of a JPanel + } + } + current_index = 0; + traversing_index = 0; + + return null; + } + }; + } + + public void bubbleSort() { + /*Bubble sorting algorithm*/ + sorter = new SwingWorker<>() { + @Override + public Void doInBackground() throws InterruptedException { + for(current_index = 0; current_index < getSIZE(); current_index++) { + for(traversing_index = 1; traversing_index < (getSIZE() - current_index); traversing_index++) { + if(bar_height[traversing_index - 1] > bar_height[traversing_index]) { + swap(traversing_index, traversing_index - 1); + traversing_index--; // just for annimation + + Thread.sleep(10); // controls the speed + repaint(); // we need it because we ofter replace the contents of a JPanel + } + } + } + current_index = 0; + traversing_index = 0; + + return null; + } + }; + } + + public void mergeSort() { + /*Merge sorting algorithm*/ + // Change code from bubbleSort to mergeSort + // TODO + + sorter = new SwingWorker<>() { + @Override + public Void doInBackground() throws InterruptedException { + for(current_index = 0; current_index < getSIZE(); current_index++) { + for(traversing_index = 1; traversing_index < (getSIZE() - current_index); traversing_index++) { + if(bar_height[traversing_index - 1] > bar_height[traversing_index]) { + swap(traversing_index, traversing_index - 1); + traversing_index--; // just for annimation + + Thread.sleep(10); // controls the speed + repaint(); // we need it because we ofter replace the contents of a JPanel + } + } + } + current_index = 0; + traversing_index = 0; + + return null; + } + }; + } + + + public void selectionSort() { + /*Merge sorting algorithm*/ + // Change code from bubbleSort to mergeSort + // TODO + + sorter = new SwingWorker<>() { + @Override + public Void doInBackground() throws InterruptedException { + for(current_index = 0; current_index < getSIZE() - 1; current_index++) { + int min_index = current_index; + for(int traversing_index = current_index + 1; traversing_index < getSIZE(); traversing_index++) { + if (bar_height[traversing_index] < bar_height[min_index]) { + min_index = traversing_index; + } + } + swap(current_index, min_index); + Thread.sleep(10); // controls the speed + repaint(); // we need it because we ofter replace the contents of a JPanel + } + + current_index = 0; + traversing_index = 0; + + return null; + } + }; + } + + public void initShuffler() { + /*Shuffles each bar*/ + shuffler = new SwingWorker<>() { + @Override + public Void doInBackground() throws InterruptedException { + int middle = getSIZE() / 2; + for (int i = 0, j = middle; i < middle; i++, j++) { + int randow_index = new Random().nextInt(getSIZE()); + swap(i, randow_index); + + randow_index = new Random().nextInt(getSIZE()); + swap(j, randow_index); + + Thread.sleep(10); // controls the speed + repaint(); // we need it because we ofter replace the contents of a JPanel + } + return null; + } + // after finishing the process + @Override + public void done() { + super.done(); + sorter.execute(); + } + }; + shuffler.execute(); + } + + public void initBarHeight() { + /*Initialize the height of each bar*/ + float interval = (float)HEIGHT / getSIZE(); + for(int i = 0; i < getSIZE(); i++) { + bar_height[i] = i * interval; + } + } + + public void swap(int indexA, int indexB) { + /*Swaps the elements*/ + float temp = bar_height[indexA]; + bar_height[indexA] = bar_height[indexB]; + bar_height[indexB] = temp; + } +} \ No newline at end of file diff --git a/assets/mvp1.gif b/assets/mvp1.gif new file mode 100644 index 0000000..902b36e Binary files /dev/null and b/assets/mvp1.gif differ diff --git a/examples/InsertionSort.txt b/examples/InsertionSort.txt new file mode 100644 index 0000000..758a476 --- /dev/null +++ b/examples/InsertionSort.txt @@ -0,0 +1,128 @@ +package examples; + +// Insertion sort visualizer + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; +import java.util.Random; + +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; + +public class InsertionSort extends JPanel { + private final int WIDTH = 800, HEIGHT = WIDTH * 9 /16; + private final int SIZE = 200; // the number if sorting elements + private final float BAR_WIDTH = (float)WIDTH / SIZE; // bar width + private float[] bar_height = new float[SIZE]; // height of bars + private SwingWorker shuffler, sorter; + private int current_index, traversing_index; + + InsertionSort() { + setBackground(Color.BLACK); + setPreferredSize(new Dimension(WIDTH, HEIGHT)); + initBarHeight(); // initialize the height of each bar + initSorter(); + initShuffler(); + } + + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + + Graphics2D g2d = (Graphics2D)g; + g2d.setColor(Color.CYAN); + Rectangle2D.Float bar; + for(int i = 0; i < SIZE; i++ ) { + bar = new Rectangle2D.Float(i * BAR_WIDTH, 0, BAR_WIDTH, bar_height[i]); + g2d.fill(bar); // g2d.draw(bar); + } + + g2d.setColor(Color.RED); + bar = new Rectangle2D.Float(current_index * BAR_WIDTH, 0, BAR_WIDTH, bar_height[current_index]); + g2d.fill(bar); + + g2d.setColor(Color.GREEN); + bar = new Rectangle2D.Float(traversing_index * BAR_WIDTH, 0, BAR_WIDTH, bar_height[traversing_index]); + g2d.fill(bar); + } + + public void initBarHeight() { + float interval = (float)HEIGHT / SIZE; + for(int i = 0; i < SIZE; i++) { + bar_height[i] = i * interval; + } + } + + public void initSorter() { + sorter = new SwingWorker<>() { + @Override + public Void doInBackground() throws InterruptedException { + for(current_index = 1; current_index < SIZE; current_index++) { + traversing_index = current_index; + while(traversing_index > 0 && bar_height[traversing_index] < bar_height[traversing_index - 1]) { + swap(traversing_index, traversing_index - 1); + traversing_index--; + + Thread.sleep(1); + repaint(); + } + } + current_index = 0; + traversing_index = 0; + + return null; + } + }; + } + + public void initShuffler() { + shuffler = new SwingWorker<>() { + @Override + public Void doInBackground() throws InterruptedException { + int middle = SIZE / 2; + for (int i = 0, j = middle; i < middle; i++, j++) { + int randow_index = new Random().nextInt(SIZE); + swap(i, randow_index); + + randow_index = new Random().nextInt(SIZE); + swap(j, randow_index); + + Thread.sleep(10); + repaint(); + } + return null; + } + + @Override + public void done() { + super.done(); + sorter.execute(); + } + }; + shuffler.execute(); + } + + public void swap(int indexA, int indexB) { + float temp = bar_height[indexA]; + bar_height[indexA] = bar_height[indexB]; + bar_height[indexB] = temp; + } + + public static void main(String args[]) { + SwingUtilities.invokeLater(() -> { + JFrame frame = new JFrame("Insertion Sort"); + //frame.setResizable(false); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setContentPane(new InsertionSort()); + frame.validate(); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); + } +}