└── src ├── input └── Input.java ├── sound └── Oscillator.java ├── sorts └── Sorts.java └── window ├── SettingsPane.java └── Window.java /src/input/Input.java: -------------------------------------------------------------------------------- 1 | package input; 2 | 3 | import java.awt.Point; 4 | import java.awt.event.KeyEvent; 5 | import java.awt.event.KeyListener; 6 | import java.awt.event.MouseEvent; 7 | import java.awt.event.MouseListener; 8 | import java.awt.event.MouseMotionListener; 9 | 10 | public class Input implements KeyListener, MouseListener, MouseMotionListener { 11 | 12 | public static final Input singleton = new Input(); 13 | 14 | public static Point mousePosition = new Point(0,0); 15 | public static boolean isDragging = false; 16 | 17 | public void mouseDragged(MouseEvent e) { 18 | mousePosition = e.getPoint(); 19 | } 20 | public void mouseMoved(MouseEvent e) { 21 | mousePosition = e.getPoint(); 22 | } 23 | public void mousePressed(MouseEvent e) { 24 | isDragging = true; 25 | } 26 | public void mouseReleased(MouseEvent e) { 27 | isDragging = false; 28 | } 29 | public void keyPressed(KeyEvent e) { 30 | 31 | } 32 | public void keyReleased(KeyEvent e) { 33 | 34 | } 35 | 36 | 37 | // Unused interface methods // 38 | 39 | public void mouseClicked(MouseEvent e) {} 40 | public void mouseEntered(MouseEvent e) {} 41 | public void mouseExited(MouseEvent e) {} 42 | public void keyTyped(KeyEvent e) {} 43 | } 44 | -------------------------------------------------------------------------------- /src/sound/Oscillator.java: -------------------------------------------------------------------------------- 1 | package sound; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import javax.sound.sampled.AudioFormat; 5 | import javax.sound.sampled.AudioInputStream; 6 | import javax.sound.sampled.AudioSystem; 7 | import javax.sound.sampled.Clip; 8 | import javax.sound.sampled.LineUnavailableException; 9 | 10 | public class Oscillator { 11 | 12 | private static Clip clip; 13 | 14 | private static final float SAMPLE_RATE = 22000; 15 | 16 | public static void beep(int duration, int scalar) { 17 | try { 18 | generateTone(100 + (int)(scalar * 2)); 19 | loop(duration); 20 | } catch (LineUnavailableException e) { 21 | e.printStackTrace(); 22 | } 23 | } 24 | 25 | private static void generateTone(int freq) throws LineUnavailableException { 26 | clip = AudioSystem.getClip(); 27 | 28 | int intFPW = getFpw(freq); 29 | 30 | int wavelengths = 20; 31 | byte[] buf = new byte[2 * intFPW * wavelengths]; 32 | AudioFormat af = new AudioFormat(SAMPLE_RATE, 8, 2, true, false); 33 | 34 | int maxVol = 127; 35 | for (int i = 0; i < intFPW * wavelengths; i++) { 36 | double angle = ((float) (i * 2) / ((float) intFPW)) * (Math.PI); 37 | buf[i * 2] = getByteValue(angle); 38 | buf[(i * 2) + 1] = buf[i * 2]; 39 | } 40 | 41 | try { 42 | byte[] b = buf; 43 | AudioInputStream ais = new AudioInputStream(new ByteArrayInputStream(b), af, buf.length / 2); 44 | 45 | clip.open(ais); 46 | } catch (Exception e) { 47 | e.printStackTrace(); 48 | } 49 | } 50 | private static int getFpw(int freq) { 51 | return (int) (SAMPLE_RATE / freq); 52 | // 6 / x = 2 => 6 / 2 = x => sr / freq = fpw 53 | } 54 | 55 | private static void loopSound(boolean commence) { 56 | if (commence) { 57 | clip.setFramePosition(0); 58 | clip.loop(Clip.LOOP_CONTINUOUSLY); 59 | } else { 60 | clip.stop(); 61 | } 62 | } 63 | 64 | private static void loop(int loops) { 65 | clip.loop(loops); 66 | } 67 | 68 | private static byte getByteValue(double angle) { 69 | int maxVol = 127; 70 | return (new Integer((int) Math.round(Math.sin(angle) * maxVol))).byteValue(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/sorts/Sorts.java: -------------------------------------------------------------------------------- 1 | package sorts; 2 | 3 | import java.util.Arrays; 4 | 5 | public abstract class Sorts { 6 | private static BubbleSort bs; 7 | private static InsertionSort is; 8 | 9 | public static final ISort bubbleSort() { 10 | if(bs == null) { 11 | bs = new BubbleSort(); 12 | } 13 | return bs; 14 | } 15 | public static final ISort insertionSort() { 16 | if(is == null) { 17 | is = new InsertionSort(); 18 | } 19 | return is; 20 | } 21 | 22 | public static ISort[] getAllSortingAlgos() { 23 | return new ISort[] {bubbleSort(), insertionSort()}; 24 | } 25 | 26 | private Sorts() {} 27 | public static interface ISort { 28 | public boolean sortOneIteration(int[] arr); 29 | public long arrayAccesses(); 30 | public long comparisons(); 31 | public int[] getCurrentComparing(); 32 | public void reset(); 33 | } 34 | 35 | private static class BubbleSort implements ISort { 36 | private int i = 0, j = 0; 37 | private long aa = 0, cmp = 0; 38 | public boolean sortOneIteration(int[] arr) { 39 | int n = arr.length; 40 | 41 | if (arr[j] > arr[j + 1]) { 42 | swap(arr, j + 1, j); 43 | aa += 2 + 4; // if and swap(); 44 | cmp += 1; // if (idk if for loops count) 45 | } 46 | 47 | j++; 48 | if(!(j < n - i - 1)) {i++; j = 0;} 49 | if(!(i < n - 1)) { 50 | return true; 51 | } 52 | 53 | return false; 54 | } 55 | public long arrayAccesses() { 56 | return aa; 57 | } 58 | public long comparisons() { 59 | return cmp; 60 | } 61 | public void reset() { 62 | i = 0; j = 0; aa = 0; cmp = 0; 63 | } 64 | public int[] getCurrentComparing() { 65 | return new int[] {j, j + 1}; 66 | } 67 | public String toString() { 68 | return this.getClass().getSimpleName(); 69 | } 70 | } 71 | 72 | private static class InsertionSort implements ISort { 73 | 74 | private int j = 1, i = 0, key = 0; 75 | boolean whilee = true; 76 | 77 | private long aa = 0, cmp = 0; 78 | public boolean sortOneIteration(int[] array) { 79 | if(whilee) { 80 | key = array[j]; 81 | i = j - 1; 82 | 83 | aa++; 84 | } 85 | whilee = false; 86 | 87 | if(i >= 0 && key < array[i]) { 88 | array[i + 1] = array[i]; 89 | i--; 90 | 91 | cmp++; 92 | aa += 3; 93 | return false; 94 | } 95 | whilee = true; 96 | 97 | array[i + 1] = key; 98 | aa++; 99 | 100 | j++; 101 | if (j < array.length) { 102 | return false; 103 | } else { 104 | return true; 105 | } 106 | } 107 | public long arrayAccesses() { 108 | return aa; 109 | } 110 | public long comparisons() { 111 | return cmp; 112 | } 113 | public void reset() { 114 | j = 1; i = 0; key = 0; whilee = true; 115 | aa = 0; cmp = 0; 116 | } 117 | public String toString() { 118 | return this.getClass().getSimpleName(); 119 | } 120 | public int[] getCurrentComparing() { 121 | System.out.println("j - "+j); 122 | System.out.println("i - "+i); 123 | return new int[] {j, i}; 124 | } 125 | } 126 | 127 | private static void swap(int[] arr, int ind1, int ind2) { 128 | int tmp = arr[ind1]; 129 | arr[ind1] = arr[ind2]; 130 | arr[ind2] = tmp; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/window/SettingsPane.java: -------------------------------------------------------------------------------- 1 | package window; 2 | 3 | import java.awt.Dimension; 4 | import java.awt.Insets; 5 | import java.awt.event.ActionEvent; 6 | import java.awt.event.ActionListener; 7 | import javax.swing.JButton; 8 | import javax.swing.JCheckBox; 9 | import javax.swing.JLabel; 10 | import javax.swing.JList; 11 | import javax.swing.JPanel; 12 | import javax.swing.JSeparator; 13 | import javax.swing.JSpinner; 14 | import javax.swing.ListSelectionModel; 15 | import javax.swing.SpinnerNumberModel; 16 | import javax.swing.event.ChangeEvent; 17 | import javax.swing.event.ChangeListener; 18 | 19 | import sorts.Sorts; 20 | import sorts.Sorts.ISort; 21 | 22 | public class SettingsPane extends JPanel { 23 | 24 | private final ActionListener START_AL = new ActionListener() { 25 | public void actionPerformed(ActionEvent e) { 26 | Window.startSorting(sortList.getSelectedValue()); // Swap if bugs happen. 27 | Window.setDelay((Double)delaySpinner.getValue()); // ^ 28 | stop.setEnabled(true); 29 | start.setEnabled(false); 30 | } 31 | }; 32 | private final ActionListener STOP_AL = new ActionListener() { 33 | public void actionPerformed(ActionEvent e) { 34 | Window.pauseSorting(); 35 | start.setEnabled(true); 36 | stop.setEnabled(false); 37 | } 38 | }; 39 | private final ActionListener RND_AL = new ActionListener() { 40 | public void actionPerformed(ActionEvent e) { 41 | Window.setNewArray(Window.genRandomArr(elementAmt, Window.HEIGHT-20)); 42 | if(Window.isSorting()) { 43 | Window.stopSorting(); 44 | start.setEnabled(true); 45 | stop.setEnabled(false); 46 | } else { 47 | start.setEnabled(true); 48 | } 49 | } 50 | }; 51 | 52 | private JButton rnd; 53 | private JButton start; 54 | private JButton stop; 55 | 56 | private JList sortList; 57 | 58 | private JSpinner delaySpinner; 59 | private JSpinner elementSpinner; 60 | 61 | private JCheckBox showMax, showMin; 62 | 63 | private int elementAmt = 100; 64 | 65 | public SettingsPane() { 66 | setLayout(null); 67 | setPreferredSize(new Dimension(150, Window.HEIGHT)); 68 | JLabel l = new JLabel("Sort Visualizer"); 69 | l.setBounds(35, 5, 100, 25); 70 | add(l); 71 | JSeparator s = new JSeparator(JSeparator.HORIZONTAL); 72 | s.setBounds(0, 35, 150, 50); 73 | add(s); 74 | 75 | start = new JButton("Start"); 76 | start.setMargin(new Insets(0, 0, 0, 0)); 77 | start.setBounds(16, 55, 50, 25); 78 | start.setEnabled(false); 79 | stop = new JButton("Stop"); 80 | stop.setMargin(new Insets(0, 0, 0, 0)); 81 | stop.setBounds(71+12, 55, 50, 25); 82 | stop.setEnabled(false); 83 | rnd = new JButton("Random array"); 84 | rnd.setBounds(4+12, 90, 117, 25); 85 | 86 | start.addActionListener(START_AL); 87 | stop.addActionListener(STOP_AL); 88 | rnd.addActionListener(RND_AL); 89 | 90 | sortList = new JList<>(Sorts.getAllSortingAlgos()); 91 | sortList.setSelectedIndex(0); 92 | sortList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 93 | sortList.setBounds(4, 125, 142, 120); 94 | add(sortList); 95 | 96 | delaySpinner = new JSpinner(new SpinnerNumberModel(100, 0.01, 1000, 0.01)); 97 | delaySpinner.setBounds(4, 250, 50, 25); 98 | delaySpinner.addChangeListener(new ChangeListener() { 99 | public void stateChanged(ChangeEvent e) { 100 | Window.setDelay((double) delaySpinner.getValue()); 101 | } 102 | }); 103 | add(delaySpinner); 104 | JLabel delspl = new JLabel("Delay (ms)"); 105 | delspl.setBounds(63, 250, 100, 25); 106 | add(delspl); 107 | 108 | elementSpinner = new JSpinner(new SpinnerNumberModel(100, 10, 640, 1)); // TODO: Fix >640 bug 109 | elementSpinner.setBounds(4, 280, 50, 25); 110 | elementSpinner.addChangeListener(new ChangeListener() { 111 | public void stateChanged(ChangeEvent e) { 112 | elementAmt = (int) elementSpinner.getValue(); 113 | } 114 | }); 115 | add(elementSpinner); 116 | JLabel elspl = new JLabel("Elements"); 117 | elspl.setBounds(63, 280, 100, 25); 118 | add(elspl); 119 | 120 | 121 | showMax = new JCheckBox("Show max"); 122 | showMax.setSelected(true); 123 | showMax.addActionListener((ev) -> Window.setShowMax(showMax.isSelected())); 124 | showMax.setBounds(1, 310, 100, 25); 125 | add(showMax); 126 | 127 | showMin = new JCheckBox("Show min"); 128 | showMin.setSelected(true); 129 | showMin.setBounds(1, 335, 100, 25); 130 | showMin.addActionListener((ev) -> Window.setShowMin(showMin.isSelected())); 131 | add(showMin); 132 | 133 | add(start); 134 | add(stop); 135 | add(rnd); 136 | } 137 | 138 | public void finishedCallback() { 139 | STOP_AL.actionPerformed(null); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/window/Window.java: -------------------------------------------------------------------------------- 1 | package window; 2 | 3 | import java.awt.BorderLayout; 4 | import java.awt.Canvas; 5 | import java.awt.Color; 6 | import java.awt.Dimension; 7 | import java.awt.Font; 8 | import java.awt.Graphics; 9 | import java.awt.Graphics2D; 10 | import java.awt.RenderingHints; 11 | import java.awt.image.BufferStrategy; 12 | import java.util.Random; 13 | 14 | import javax.swing.JFrame; 15 | import javax.swing.JPanel; 16 | 17 | import input.Input; 18 | import sorts.Sorts; 19 | import sorts.Sorts.ISort; 20 | import sound.Oscillator; 21 | 22 | public class Window { 23 | public static void main(String[] args) {Window.init(); Window.entry();} 24 | 25 | public static final int WIDTH = 1280, HEIGHT = 500; 26 | public static int STATUS_X = 10, STATUS_Y = 10; 27 | 28 | private static int padding = 1; 29 | private static int renderOffset = 0; 30 | private static double loopDelay = 500.0 * 1e6; 31 | private static double msDelay = 0; 32 | private static boolean showMax = true, showMin = true; 33 | private static int[] tosort; 34 | 35 | private static boolean sorting = false; 36 | private static ISort currentSort = Sorts.bubbleSort(); 37 | 38 | 39 | 40 | private static JFrame frame; 41 | private static SettingsPane settingsPane; 42 | private static Canvas canvas; 43 | 44 | private static BufferStrategy bs; 45 | private static Graphics g; 46 | 47 | public static void init() { 48 | frame = new JFrame(); 49 | frame.setTitle("Sort Visualization"); 50 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 51 | frame.setResizable(false); 52 | frame.setSize(WIDTH, HEIGHT); 53 | frame.setLocationRelativeTo(null); 54 | frame.setFocusable(true); 55 | frame.requestFocus(); 56 | frame.requestFocusInWindow(); 57 | frame.addKeyListener(Input.singleton); 58 | frame.addMouseListener(Input.singleton); 59 | frame.addMouseMotionListener(Input.singleton); 60 | JPanel cp = new JPanel(); 61 | cp.setLayout(new BorderLayout()); 62 | frame.setContentPane(cp); 63 | frame.setVisible(true); 64 | 65 | canvas = new Canvas(); 66 | canvas.setPreferredSize(new Dimension(Window.WIDTH, Window.HEIGHT)); 67 | canvas.addKeyListener(Input.singleton); 68 | canvas.addMouseListener(Input.singleton); 69 | canvas.addMouseMotionListener(Input.singleton); 70 | settingsPane = new SettingsPane(); 71 | frame.add(settingsPane, BorderLayout.EAST); 72 | frame.add(canvas, BorderLayout.WEST); 73 | canvas.createBufferStrategy(1); 74 | bs = canvas.getBufferStrategy(); 75 | g = bs.getDrawGraphics(); 76 | 77 | frame.pack(); 78 | } 79 | 80 | 81 | 82 | private static void render() { 83 | if(tosort != null) { 84 | g.translate(renderOffset, 0); 85 | 86 | int max = 0; 87 | int min = tosort.length; 88 | int[] cmp = currentSort.getCurrentComparing(); 89 | 90 | if(showMax) 91 | for (int i = 0; i < tosort.length; i++) { 92 | max = Math.max(max, tosort[i]); 93 | } 94 | 95 | if(showMin) 96 | for (int i = 0; i < tosort.length; i++) { 97 | min = Math.min(min, tosort[i]); 98 | } 99 | g.setColor(Color.WHITE); 100 | for (int i = 0; i < tosort.length; i++) { 101 | if (tosort[i] == max) { 102 | if(showMax) { 103 | g.setColor(Color.BLUE); 104 | g.fillRect(i * padding, HEIGHT - tosort[i], padding, tosort[i]); 105 | g.setColor(Color.WHITE); 106 | max = -1; 107 | } 108 | } else if (tosort[i] == min) { 109 | if(showMin) { 110 | g.setColor(Color.CYAN); 111 | g.fillRect(i * padding, HEIGHT - tosort[i], padding, tosort[i]); 112 | g.setColor(Color.WHITE); 113 | min = -1; 114 | } 115 | } else { 116 | g.fillRect(i * padding, HEIGHT - tosort[i], padding, tosort[i]); 117 | for (int j = 0; j < cmp.length; j++) { 118 | if(i == cmp[j]) { 119 | g.setColor(Color.RED); 120 | g.fillRect(i * padding, HEIGHT - tosort[i], padding, tosort[i]); 121 | g.setColor(Color.WHITE); 122 | } 123 | continue; 124 | } 125 | } 126 | g.setColor(Color.BLACK); 127 | g.drawRect(i * padding, HEIGHT - tosort[i], padding, tosort[i]); 128 | g.setColor(Color.WHITE); 129 | } 130 | } 131 | g.setFont(new Font("Consolas", Font.PLAIN, 13)); 132 | ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 133 | g.setColor(Color.WHITE); 134 | g.drawString("Elements: " + ((tosort == null ? "n/a" : tosort.length) + " Array accesses: " 135 | + currentSort.arrayAccesses() + " Comparisons: " + currentSort.comparisons() + " Delay: " + msDelay), 136 | STATUS_X, STATUS_Y); 137 | } 138 | 139 | private static boolean fin = false; 140 | private static void tick() { 141 | if(sorting) { 142 | if(currentSort.sortOneIteration(tosort)) { 143 | //settingsPane.finishedCallback(); 144 | } 145 | int[] arr = currentSort.getCurrentComparing(); 146 | for (int i : arr) { 147 | Oscillator.beep(2, tosort[Math.max(1, i)-1]); 148 | } 149 | } 150 | } 151 | 152 | public static void setNewArray(int[] array) { 153 | tosort = array; 154 | 155 | padding = (WIDTH) / (tosort.length); 156 | 157 | renderOffset = (WIDTH - (tosort.length * padding)) / 2; 158 | } 159 | 160 | 161 | private static double delta_tick = 0; 162 | private static long now_tick; 163 | private static long last_tick = System.nanoTime(); 164 | private static void entry() { 165 | final double times_fps = 1e9 / 60; 166 | 167 | double delta_render = 0; 168 | long now_render; 169 | long last_render = System.nanoTime(); 170 | 171 | preinit(); 172 | 173 | while(true) { 174 | now_render = System.nanoTime(); 175 | delta_render += (now_render - last_render) / times_fps; 176 | last_render = now_render; 177 | 178 | now_tick = System.nanoTime(); 179 | delta_tick += (now_tick - last_tick) / (loopDelay); 180 | last_tick = now_tick; 181 | 182 | if (delta_tick >= 1) { 183 | tick(); 184 | delta_tick--; 185 | } 186 | if (delta_render >= 1) { 187 | g.setColor(Color.BLACK); 188 | g.fillRect(0, 0, WIDTH, HEIGHT); 189 | 190 | ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 191 | render(); 192 | 193 | if(tosort == null) { 194 | g.setColor(Color.WHITE); 195 | g.setFont(new Font("Monospaced", Font.BOLD, 24)); 196 | g.drawString("Press \"Generate Random\" to create a new array!", WIDTH/2 - 325, HEIGHT / 2); 197 | } 198 | 199 | g.dispose(); 200 | bs.show(); 201 | g = bs.getDrawGraphics(); 202 | 203 | delta_render--; 204 | } 205 | } 206 | } 207 | 208 | public static void startSorting(ISort sort) { 209 | currentSort = sort; 210 | currentSort.reset(); 211 | 212 | sorting = true; 213 | } 214 | 215 | public static void setDelay(double msDelay) { 216 | loopDelay = msDelay * 1e6; 217 | Window.msDelay = msDelay; 218 | delta_tick = 0; 219 | } 220 | 221 | public static void setShowMax(boolean show) { 222 | showMax = show; 223 | } 224 | 225 | public static void setShowMin(boolean show) { 226 | showMin = show; 227 | } 228 | 229 | public static void pauseSorting() { 230 | sorting = false; 231 | } 232 | 233 | public static void stopSorting() { 234 | sorting = false; 235 | currentSort.reset(); 236 | } 237 | 238 | public static boolean isSorting() { 239 | return sorting; 240 | } 241 | 242 | // Clear bg a couple of times, so there will be no blinking at low framerates 243 | private static void preinit() { 244 | for (int i = 0; i < 15; i++) { 245 | g.setColor(Color.BLACK); 246 | g.fillRect(0, 0, WIDTH, HEIGHT); 247 | g.dispose(); 248 | bs.show(); 249 | g = bs.getDrawGraphics(); 250 | } 251 | } 252 | 253 | public static int[] genRandomArr(int len, int highBound) { 254 | Random r = new Random(); 255 | int[] arr = new int[len]; 256 | for (int i = 0; i < len; i++) { 257 | arr[i] = r.nextInt(highBound - 14) + 14; 258 | } 259 | return arr; 260 | } 261 | } 262 | --------------------------------------------------------------------------------