├── .gitignore ├── README.md ├── newer_ui_tailwind.JPG ├── older_ui.JPG ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── sorting_visualizer.gif ├── src ├── App.css ├── App.js ├── SortingAlgorithms │ ├── BubbleSort.js │ ├── HeapSort.js │ ├── InsertionSort.js │ ├── MergeSort.js │ ├── QuickSort.js │ └── SelectionSort.js ├── SortingVisualizer │ ├── SortingVisualizer.css │ └── SortingVisualizer.js └── index.js ├── styles.css └── tailwind.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | .cursorrules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sorting Visualizer 2 | A visualization for various sorting algorithms like merge sort, heap sort, quick sort, insertion sort, bubble sort and selection sort.
3 | Access it using this link https://csals.github.io/Sorting-Visualizer/ 4 | 5 | Rewrote it with **Cursor + Claude** to enhance the UI and complete all the TODOs 6 | 7 | ## before & after 8 | 9 | Before: 10 | ![Before](older_ui.JPG) 11 | 12 | After: 13 | ![After](newer_ui_tailwind.JPG) 14 | 15 | 16 | ## NOTES 17 | - in every sorting algo I am returning two comparisions. 18 | - that's because when I am comparing 2 bars first I will change their color to red and again need to change to original color 19 | - for that reason every time 2 bars are compared we need 2 comparisions 20 | 21 | 22 | - Huge thanks to [Clément Mihailescu](https://github.com/clementmihailescu) for this project idea. 23 | -------------------------------------------------------------------------------- /newer_ui_tailwind.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CSALS/Sorting-Visualizer/e4b5409ea5d0eb856c04bfb9a68e936bd071de44/newer_ui_tailwind.JPG -------------------------------------------------------------------------------- /older_ui.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CSALS/Sorting-Visualizer/e4b5409ea5d0eb856c04bfb9a68e936bd071de44/older_ui.JPG -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sorting-visualizer", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.11.0", 7 | "react-dom": "^16.11.0", 8 | "react-scripts": "^5.0.1" 9 | }, 10 | "homepage": "http://CSALS.github.io/Sorting-Visualizer", 11 | "scripts": { 12 | "start": "react-scripts start", 13 | "build": "react-scripts build", 14 | "test": "react-scripts test", 15 | "eject": "react-scripts eject", 16 | "predeploy": "npm run build", 17 | "deploy": "gh-pages -d build" 18 | }, 19 | "eslintConfig": { 20 | "extends": "react-app" 21 | }, 22 | "browserslist": { 23 | "production": [ 24 | ">0.2%", 25 | "not dead", 26 | "not op_mini all" 27 | ], 28 | "development": [ 29 | "last 1 chrome version", 30 | "last 1 firefox version", 31 | "last 1 safari version" 32 | ] 33 | }, 34 | "devDependencies": { 35 | "autoprefixer": "^10.4.20", 36 | "gh-pages": "^6.1.1", 37 | "postcss": "^8.4.41", 38 | "tailwindcss": "^3.4.10" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CSALS/Sorting-Visualizer/e4b5409ea5d0eb856c04bfb9a68e936bd071de44/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | Sorting Visualizer 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CSALS/Sorting-Visualizer/e4b5409ea5d0eb856c04bfb9a68e936bd071de44/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CSALS/Sorting-Visualizer/e4b5409ea5d0eb856c04bfb9a68e936bd071de44/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#fcfcfc", 24 | "background_color": "#fcfcfc" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /sorting_visualizer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CSALS/Sorting-Visualizer/e4b5409ea5d0eb856c04bfb9a68e936bd071de44/sorting_visualizer.gif -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | html,body { 6 | height:100%; 7 | width:100%; 8 | margin: 0; 9 | padding: 0; 10 | } 11 | body { 12 | background: #DAE0E2; 13 | } 14 | .App { 15 | } 16 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import SortingVisualizer from './SortingVisualizer/SortingVisualizer' 3 | import './App.css' 4 | 5 | function App() { 6 | return ( 7 |
8 | 9 |
10 | ); 11 | } 12 | 13 | export default App; -------------------------------------------------------------------------------- /src/SortingAlgorithms/BubbleSort.js: -------------------------------------------------------------------------------- 1 | export function getBubbleSortAnimations(array) { 2 | let animations = []; 3 | let auxillaryArray = array.slice(); 4 | bubbleSort(auxillaryArray, animations); 5 | const javaScriptSortedArray = array.slice().sort((a, b) => a - b); 6 | console.log("sort works correctly? ",arraysAreEqual(javaScriptSortedArray, auxillaryArray)); 7 | array = auxillaryArray; 8 | return [animations, array]; 9 | } 10 | 11 | function bubbleSort(auxillaryArray, animations) { 12 | const N = auxillaryArray.length; 13 | let iters = N - 1; 14 | while(iters > 0) { 15 | let swapped = false; 16 | for(let i = 0; i < iters; ++i) { 17 | animations.push(["comparision1", i, i + 1]); 18 | animations.push(["comparision2", i, i + 1]); 19 | if(auxillaryArray[i] > auxillaryArray[i + 1]) { 20 | swapped = true; 21 | animations.push(["swap", i, auxillaryArray[i + 1]]); 22 | animations.push(["swap", i + 1, auxillaryArray[i]]); 23 | swap(auxillaryArray, i, i + 1); 24 | } 25 | } 26 | if(swapped === false) break; 27 | iters--; 28 | } 29 | } 30 | 31 | function swap(auxillaryArray, firstIndex, secondIndex) { 32 | let temp = auxillaryArray[firstIndex]; 33 | auxillaryArray[firstIndex] = auxillaryArray[secondIndex]; 34 | auxillaryArray[secondIndex] = temp; 35 | } 36 | 37 | function arraysAreEqual(firstArray, secondArray) { 38 | if (firstArray.length !== secondArray.length) { 39 | return false; 40 | } 41 | for (let i = 0; i < firstArray.length; i++) { 42 | if (firstArray[i] !== secondArray[i]) { 43 | return false; 44 | } 45 | } 46 | return true; 47 | } -------------------------------------------------------------------------------- /src/SortingAlgorithms/HeapSort.js: -------------------------------------------------------------------------------- 1 | export function getHeapSortAnimations(array) { 2 | let animations = []; 3 | let auxillaryArray = array.slice(); 4 | heapSort(auxillaryArray, animations); 5 | const javaScriptSortedArray = array.slice().sort((a, b) => a - b); 6 | console.log("sort works correctly? ", arraysAreEqual(javaScriptSortedArray, auxillaryArray)); 7 | return [animations, auxillaryArray]; 8 | } 9 | 10 | function heapSort(auxillaryArray, animations) { 11 | const N = auxillaryArray.length; 12 | 13 | // Build heap (rearrange array) 14 | for (let i = Math.floor(N / 2) - 1; i >= 0; i--) { 15 | heapify(auxillaryArray, N, i, animations); 16 | } 17 | 18 | // One by one extract an element from heap 19 | for (let i = N - 1; i > 0; i--) { 20 | animations.push(["comparision1", 0, i]); // Color change for comparison 21 | animations.push(["comparision2", 0, i]); // Color change for comparison 22 | animations.push(["swap", 0, auxillaryArray[i]]); 23 | animations.push(["swap", i, auxillaryArray[0]]); 24 | swap(auxillaryArray, 0, i); 25 | heapify(auxillaryArray, i, 0, animations); 26 | } 27 | } 28 | 29 | function heapify(auxillaryArray, N, i, animations) { 30 | let largest = i; // Initialize largest as root 31 | let left = 2 * i + 1; // left = 2*i + 1 32 | let right = 2 * i + 2; // right = 2*i + 2 33 | 34 | // If left child is larger than root 35 | if (left < N && auxillaryArray[left] > auxillaryArray[largest]) { 36 | animations.push(["comparision1", left, largest]); // Color change for comparison 37 | animations.push(["comparision2", left, largest]); // Color change for comparison 38 | largest = left; 39 | } 40 | 41 | // If right child is larger than largest so far 42 | if (right < N && auxillaryArray[right] > auxillaryArray[largest]) { 43 | animations.push(["comparision1", right, largest]); // Color change for comparison 44 | animations.push(["comparision2", right, largest]); // Color change for comparison 45 | largest = right; 46 | } 47 | 48 | // If largest is not root 49 | if (largest !== i) { 50 | animations.push(["swap", i, auxillaryArray[largest]]); 51 | animations.push(["swap", largest, auxillaryArray[i]]); 52 | swap(auxillaryArray, i, largest); 53 | heapify(auxillaryArray, N, largest, animations); 54 | } 55 | } 56 | 57 | function swap(auxillaryArray, firstIndex, secondIndex) { 58 | let temp = auxillaryArray[firstIndex]; 59 | auxillaryArray[firstIndex] = auxillaryArray[secondIndex]; 60 | auxillaryArray[secondIndex] = temp; 61 | } 62 | 63 | function arraysAreEqual(firstArray, secondArray) { 64 | if (firstArray.length !== secondArray.length) { 65 | return false; 66 | } 67 | for (let i = 0; i < firstArray.length; i++) { 68 | if (firstArray[i] !== secondArray[i]) { 69 | return false; 70 | } 71 | } 72 | return true; 73 | } -------------------------------------------------------------------------------- /src/SortingAlgorithms/InsertionSort.js: -------------------------------------------------------------------------------- 1 | export function getInsertionSortAnimations(array) { 2 | let animations = []; 3 | let auxillaryArray = array.slice(); 4 | insertionSort(auxillaryArray, animations); 5 | const javaScriptSortedArray = array.slice().sort((a, b) => a - b); 6 | console.log("sort works correctly? ",arraysAreEqual(javaScriptSortedArray, auxillaryArray)); 7 | array = auxillaryArray; 8 | return [animations, array]; 9 | } 10 | 11 | function insertionSort(auxillaryArray, animations) { 12 | const N = auxillaryArray.length; 13 | for (let i = 1; i < N; i++) { 14 | let key = auxillaryArray[i]; 15 | let j = i - 1; 16 | animations.push(["comparision1", j, i]); 17 | animations.push(["comparision2", j, i]); 18 | while(j >= 0 && auxillaryArray[j] > key) { 19 | animations.push(["overwrite", j + 1, auxillaryArray[j]]); 20 | auxillaryArray[j + 1] = auxillaryArray[j]; 21 | j = j - 1; 22 | if(j >= 0) { 23 | animations.push(["comparision1", j, i]); 24 | animations.push(["comparision2", j, i]); 25 | } 26 | } 27 | animations.push(["overwrite", j + 1, key]); 28 | auxillaryArray[j + 1] = key; 29 | } 30 | } 31 | 32 | function arraysAreEqual(firstArray, secondArray) { 33 | if (firstArray.length !== secondArray.length) { 34 | return false; 35 | } 36 | for (let i = 0; i < firstArray.length; i++) { 37 | if (firstArray[i] !== secondArray[i]) { 38 | return false; 39 | } 40 | } 41 | return true; 42 | } -------------------------------------------------------------------------------- /src/SortingAlgorithms/MergeSort.js: -------------------------------------------------------------------------------- 1 | export function getMergeSortAnimations(array) { 2 | let animations = []; 3 | let auxillaryArray = array.slice(); 4 | mergeSort(auxillaryArray, 0, auxillaryArray.length - 1, animations); 5 | const javaScriptSortedArray = array.slice().sort((a, b) => a - b); 6 | console.log(arraysAreEqual(javaScriptSortedArray, auxillaryArray)); 7 | array = auxillaryArray; 8 | return [animations, array]; 9 | } 10 | 11 | function mergeSort(auxillaryArray, startIndex, endIndex, animations) { 12 | if(startIndex === endIndex) 13 | return; 14 | const middleIndex = Math.floor((startIndex + endIndex)/2); 15 | mergeSort(auxillaryArray, startIndex, middleIndex, animations); 16 | mergeSort(auxillaryArray, middleIndex + 1, endIndex, animations); 17 | merge(auxillaryArray, startIndex, middleIndex, endIndex, animations); 18 | } 19 | 20 | function merge(auxillaryArray, startIndex, middleIndex, endIndex, animations) { 21 | let sortArray = []; 22 | let i = startIndex; 23 | let j = middleIndex + 1; 24 | while(i <= middleIndex && j <= endIndex) { 25 | //Comparing value at ith and jth index so push them to change their color 26 | animations.push(["comparision1", i, j]); 27 | //By changing color we imply that we are comparing those two values and then again we should revert back to their original color so push them again 28 | animations.push(["comparision2", i, j]); 29 | if(auxillaryArray[i] <= auxillaryArray[j]) { 30 | sortArray.push(auxillaryArray[i++]); 31 | } 32 | else { 33 | sortArray.push(auxillaryArray[j++]); 34 | } 35 | } 36 | while(i <= middleIndex) { 37 | animations.push(["comparision1", i, i]); 38 | animations.push(["comparision2", i, i]); 39 | sortArray.push(auxillaryArray[i++]); 40 | } 41 | while(j <= endIndex) { 42 | animations.push(["comparision1", j, j]); 43 | animations.push(["comparision2", j, j]); 44 | sortArray.push(auxillaryArray[j++]); 45 | } 46 | for (let i = startIndex; i <= endIndex; i++) { 47 | animations.push(["comparision1", i, i - startIndex]); 48 | animations.push(["overwrite", i, sortArray[i - startIndex]]); 49 | animations.push(["comparision2", i, i - startIndex]); 50 | auxillaryArray[i] = sortArray[i - startIndex]; 51 | } 52 | } 53 | 54 | function arraysAreEqual(firstArray, secondArray) { 55 | if (firstArray.length !== secondArray.length) { 56 | return false; 57 | } 58 | for (let i = 0; i < firstArray.length; i++) { 59 | if (firstArray[i] !== secondArray[i]) { 60 | return false; 61 | } 62 | } 63 | return true; 64 | } -------------------------------------------------------------------------------- /src/SortingAlgorithms/QuickSort.js: -------------------------------------------------------------------------------- 1 | export function getQuickSortAnimations(array) { 2 | let animations = []; 3 | let auxillaryArray = array.slice(); 4 | quickSort(auxillaryArray, 0, auxillaryArray.length - 1, animations); 5 | const javaScriptSortedArray = array.slice().sort((a, b) => a - b); 6 | console.log("sort works correctly? ",arraysAreEqual(javaScriptSortedArray, auxillaryArray)); 7 | array = auxillaryArray; 8 | return [animations, array]; 9 | } 10 | 11 | function quickSort(auxillaryArray, startIndex, endIndex, animations) { 12 | let pivotIndex; 13 | if (startIndex < endIndex) { 14 | pivotIndex = partitionArray(auxillaryArray, startIndex, endIndex, animations); 15 | quickSort(auxillaryArray, startIndex, pivotIndex - 1, animations); 16 | quickSort(auxillaryArray, pivotIndex + 1, endIndex, animations); 17 | } 18 | } 19 | 20 | function partitionArray(auxillaryArray, startIndex, endIndex, animations) { 21 | let pivotIndex = randomIntFromInterval(startIndex, endIndex); 22 | 23 | animations.push(["comparision1", pivotIndex, endIndex]); 24 | animations.push(["swap", pivotIndex, auxillaryArray[endIndex]]); 25 | animations.push(["swap", endIndex, auxillaryArray[pivotIndex]]); 26 | animations.push(["comparision2", pivotIndex, endIndex]); 27 | swap(auxillaryArray, pivotIndex, endIndex); 28 | 29 | let lessTailIndex = startIndex; 30 | 31 | for(let i = startIndex; i < endIndex; ++i) { 32 | animations.push(["comparision1", i, endIndex]); 33 | animations.push(["comparision2", i, endIndex]); 34 | if(auxillaryArray[i] <= auxillaryArray[endIndex]) { 35 | animations.push(["comparision1", i, lessTailIndex]); 36 | animations.push(["swap", i, auxillaryArray[lessTailIndex]]); 37 | animations.push(["swap", lessTailIndex, auxillaryArray[i]]); 38 | animations.push(["comparision2", i, lessTailIndex]); 39 | swap(auxillaryArray, i, lessTailIndex); 40 | lessTailIndex++; 41 | } 42 | } 43 | animations.push(["comparision1", lessTailIndex, endIndex]); 44 | animations.push(["swap", endIndex, auxillaryArray[lessTailIndex]]); 45 | animations.push(["swap", lessTailIndex, auxillaryArray[endIndex]]); 46 | animations.push(["comparision2", lessTailIndex, endIndex]); 47 | 48 | swap(auxillaryArray, lessTailIndex, endIndex); 49 | return lessTailIndex; 50 | 51 | // let pivot = auxillaryArray[endIndex]; 52 | // let pivotIndex = startIndex; 53 | // for (let i = startIndex; i <= endIndex - 1; i++) { 54 | // animations.push([i, endIndex]); 55 | // animations.push([i, endIndex]); 56 | // if (auxillaryArray[i] <= pivot) { 57 | // //Swap these two heights 58 | // animations.push([i, auxillaryArray[pivotIndex]]); 59 | // animations.push([pivotIndex, auxillaryArray[i]]); 60 | // swap(auxillaryArray, i , pivotIndex); 61 | // pivotIndex++; 62 | // } 63 | // else { 64 | // animations.push([-1, -1]); 65 | // animations.push([-1, -1]); 66 | // } 67 | // animations.push([-1, -1]); 68 | // animations.push([-1, -1]); 69 | // } 70 | // animations.push([-1, -1]); 71 | // animations.push([-1, -1]); 72 | // animations.push([-1, -1]); 73 | // animations.push([-1, -1]); 74 | // //Swap these two heights 75 | // animations.push([pivotIndex, auxillaryArray[endIndex]]); 76 | // animations.push([endIndex, auxillaryArray[pivotIndex]]); 77 | // swap(auxillaryArray, pivotIndex, endIndex); 78 | // return pivotIndex; 79 | } 80 | 81 | function swap(auxillaryArray, firstIndex, secondIndex) { 82 | let temp = auxillaryArray[firstIndex]; 83 | auxillaryArray[firstIndex] = auxillaryArray[secondIndex]; 84 | auxillaryArray[secondIndex] = temp; 85 | } 86 | 87 | function arraysAreEqual(firstArray, secondArray) { 88 | if (firstArray.length !== secondArray.length) { 89 | return false; 90 | } 91 | for (let i = 0; i < firstArray.length; i++) { 92 | if (firstArray[i] !== secondArray[i]) { 93 | return false; 94 | } 95 | } 96 | return true; 97 | } 98 | 99 | function randomIntFromInterval(min, max) { 100 | // min and max included 101 | return Math.floor(Math.random() * (max - min + 1) + min); 102 | } -------------------------------------------------------------------------------- /src/SortingAlgorithms/SelectionSort.js: -------------------------------------------------------------------------------- 1 | export function getSelectionSortAnimations(array) { 2 | let animations = []; 3 | let auxillaryArray = array.slice(); 4 | selectionSort(auxillaryArray, animations); 5 | const javaScriptSortedArray = array.slice().sort((a, b) => a - b); 6 | console.log("sort works correctly? ",arraysAreEqual(javaScriptSortedArray, auxillaryArray)); 7 | array = auxillaryArray; 8 | return [animations, array]; 9 | } 10 | 11 | function selectionSort(auxillaryArray, animations) { 12 | const N = auxillaryArray.length; 13 | for (let i = 0; i < N - 1; i++) { 14 | let minIndex = i; //Finding minimum element in unsorted array 15 | for (let j = i + 1; j < N; j++) { 16 | animations.push(["comparision1", j, minIndex]); 17 | animations.push(["comparision2", j, minIndex]); 18 | if (auxillaryArray[j] < auxillaryArray[minIndex]) { 19 | minIndex = j; 20 | } 21 | } 22 | animations.push(["swap", minIndex, auxillaryArray[i]]); 23 | animations.push(["swap", i, auxillaryArray[minIndex]]); 24 | // Swap the found minimum element with the first element 25 | swap(auxillaryArray, minIndex, i); 26 | } 27 | } 28 | 29 | function swap(auxillaryArray, firstIndex, secondIndex) { 30 | let temp = auxillaryArray[firstIndex]; 31 | auxillaryArray[firstIndex] = auxillaryArray[secondIndex]; 32 | auxillaryArray[secondIndex] = temp; 33 | } 34 | 35 | function arraysAreEqual(firstArray, secondArray) { 36 | if (firstArray.length !== secondArray.length) { 37 | return false; 38 | } 39 | for (let i = 0; i < firstArray.length; i++) { 40 | if (firstArray[i] !== secondArray[i]) { 41 | return false; 42 | } 43 | } 44 | return true; 45 | } -------------------------------------------------------------------------------- /src/SortingVisualizer/SortingVisualizer.css: -------------------------------------------------------------------------------- 1 | .array-bar { 2 | box-shadow: 0 0 10px rgba(56, 189, 248, 0.5); 3 | } -------------------------------------------------------------------------------- /src/SortingVisualizer/SortingVisualizer.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from 'react'; 2 | import { getMergeSortAnimations } from '../SortingAlgorithms/MergeSort'; 3 | import { getQuickSortAnimations } from '../SortingAlgorithms/QuickSort'; 4 | import { getBubbleSortAnimations } from '../SortingAlgorithms/BubbleSort'; 5 | import { getInsertionSortAnimations } from '../SortingAlgorithms/InsertionSort'; 6 | import { getSelectionSortAnimations } from '../SortingAlgorithms/SelectionSort'; 7 | import { getHeapSortAnimations } from '../SortingAlgorithms/HeapSort'; 8 | 9 | // Constants 10 | let WINDOW_WIDTH = window.innerWidth; 11 | let WINDOW_HEIGHT = window.innerHeight; 12 | let NUMBER_OF_ARRAY_BARS = Math.floor(WINDOW_WIDTH / 8); 13 | const ANIMATION_SPEED_OPTIONS = { 14 | '0.25x': 150, 15 | '0.5x': 75, 16 | '1x': 5, 17 | '1.5x': 2.5, 18 | '2x': 1.5, 19 | }; 20 | const PRIMARY_COLOR = '#2db5a3'; // Darker teal color 21 | const SECONDARY_COLOR = '#f43f5e'; // Darker red color 22 | const BACKGROUND_COLOR = '#1e293b'; // Darker background color 23 | 24 | const SortingVisualizer = () => { 25 | const [array, setArray] = useState([]); 26 | const [selectedAlgorithm, setSelectedAlgorithm] = useState(''); 27 | const [isSorting, setIsSorting] = useState(false); // Track sorting state 28 | const [playbackSpeed, setPlaybackSpeed] = useState('1x'); // New state for playback speed 29 | const timeoutsRef = useRef([]); // Store timeouts 30 | 31 | useEffect(() => { 32 | resetArray(); 33 | window.addEventListener('resize', handleResize); 34 | return () => window.removeEventListener('resize', handleResize); 35 | }, []); 36 | 37 | const handleResize = () => { 38 | WINDOW_WIDTH = window.innerWidth; 39 | WINDOW_HEIGHT = window.innerHeight; 40 | NUMBER_OF_ARRAY_BARS = Math.floor(WINDOW_WIDTH / 8); 41 | resetArray(); 42 | }; 43 | 44 | const resetArray = () => { 45 | if (isSorting) { 46 | clearTimeouts(); // Clear ongoing animations 47 | setIsSorting(false); 48 | resetBarColors(); // Reset bar colors 49 | } 50 | const newArray = []; 51 | for (let i = 0; i < NUMBER_OF_ARRAY_BARS; i++) { 52 | newArray.push(randomIntFromInterval(5, WINDOW_HEIGHT / 2)); 53 | } 54 | setArray(newArray); 55 | }; 56 | 57 | const resetBarColors = () => { 58 | const arrayBars = document.getElementsByClassName('array-bar'); 59 | for (let i = 0; i < arrayBars.length; i++) { 60 | arrayBars[i].style.backgroundColor = PRIMARY_COLOR; // Reset to primary color 61 | } 62 | }; 63 | 64 | const disableSortButtons = () => { 65 | // No buttons to disable since we removed them 66 | }; 67 | 68 | const restoreStoreButtons = () => { 69 | // No buttons to restore since we removed them 70 | }; 71 | 72 | const mergeSort = () => { 73 | disableSortButtons(); 74 | const [animations, sortArray] = getMergeSortAnimations(array); 75 | animateSort(animations); 76 | const RESTORE_TIME = parseInt(ANIMATION_SPEED_OPTIONS[playbackSpeed] * animations.length / 2 + 3000); 77 | setTimeout(() => restoreStoreButtons(), RESTORE_TIME); 78 | }; 79 | 80 | const quickSort = () => { 81 | disableSortButtons(); 82 | const [animations, sortArray] = getQuickSortAnimations(array); 83 | animateSort(animations); 84 | const RESTORE_TIME = parseInt(ANIMATION_SPEED_OPTIONS[playbackSpeed] * animations.length / 2 + 3000); 85 | setTimeout(() => restoreStoreButtons(), RESTORE_TIME); 86 | }; 87 | 88 | const bubbleSort = () => { 89 | disableSortButtons(); 90 | const [animations, sortArray] = getBubbleSortAnimations(array); 91 | animateSort(animations); 92 | const RESTORE_TIME = parseInt(ANIMATION_SPEED_OPTIONS[playbackSpeed] * animations.length / 2 + 3000); 93 | setTimeout(() => restoreStoreButtons(), RESTORE_TIME); 94 | }; 95 | 96 | const insertionSort = () => { 97 | disableSortButtons(); 98 | const [animations, sortArray] = getInsertionSortAnimations(array); 99 | animateSort(animations); 100 | const RESTORE_TIME = parseInt(ANIMATION_SPEED_OPTIONS[playbackSpeed] * animations.length / 2 + 3000); 101 | setTimeout(() => restoreStoreButtons(), RESTORE_TIME); 102 | }; 103 | 104 | const selectionSort = () => { 105 | disableSortButtons(); 106 | const [animations, sortArray] = getSelectionSortAnimations(array); 107 | animateSort(animations); 108 | const RESTORE_TIME = parseInt(ANIMATION_SPEED_OPTIONS[playbackSpeed] * animations.length / 2 + 3000); 109 | setTimeout(() => restoreStoreButtons(), RESTORE_TIME); 110 | }; 111 | 112 | const heapSort = () => { 113 | disableSortButtons(); 114 | const [animations, sortArray] = getHeapSortAnimations(array); 115 | animateSort(animations); 116 | const RESTORE_TIME = parseInt(ANIMATION_SPEED_OPTIONS[playbackSpeed] * animations.length / 2 + 3000); 117 | setTimeout(() => restoreStoreButtons(), RESTORE_TIME); 118 | }; 119 | 120 | const handleAlgorithmChange = (e) => { 121 | setSelectedAlgorithm(e.target.value); 122 | }; 123 | 124 | const handlePlaybackSpeedChange = (e) => { 125 | setPlaybackSpeed(e.target.value); 126 | }; 127 | 128 | const executeSelectedSort = () => { 129 | if (!selectedAlgorithm) return; 130 | 131 | disableSortButtons(); 132 | switch (selectedAlgorithm) { 133 | case 'mergeSort': 134 | mergeSort(); 135 | break; 136 | case 'quickSort': 137 | quickSort(); 138 | break; 139 | case 'bubbleSort': 140 | bubbleSort(); 141 | break; 142 | case 'insertionSort': 143 | insertionSort(); 144 | break; 145 | case 'selectionSort': 146 | selectionSort(); 147 | break; 148 | case 'heapSort': 149 | heapSort(); 150 | break; 151 | default: 152 | restoreStoreButtons(); 153 | } 154 | }; 155 | 156 | const animateSort = (animations) => { 157 | setIsSorting(true); // Set sorting state to true 158 | const speedMultiplier = ANIMATION_SPEED_OPTIONS[playbackSpeed]; // Get speed multiplier 159 | for (let i = 0; i < animations.length; i++) { 160 | const arrayBars = document.getElementsByClassName('array-bar'); 161 | const isColorChange = animations[i][0] === "comparision1" || animations[i][0] === "comparision2"; 162 | if (isColorChange) { 163 | const color = animations[i][0] === "comparision1" ? SECONDARY_COLOR : PRIMARY_COLOR; 164 | const [, barOneIndex, barTwoIndex] = animations[i]; 165 | const barOneStyle = arrayBars[barOneIndex].style; 166 | const barTwoStyle = arrayBars[barTwoIndex].style; 167 | const timeoutId = setTimeout(() => { 168 | barOneStyle.backgroundColor = color; 169 | barTwoStyle.backgroundColor = color; 170 | }, i * speedMultiplier); 171 | timeoutsRef.current.push(timeoutId); // Store timeout 172 | } else { 173 | const [, barIndex, newHeight] = animations[i]; 174 | if (barIndex === -1) { 175 | continue; 176 | } 177 | const barStyle = arrayBars[barIndex].style; 178 | const timeoutId = setTimeout(() => { 179 | barStyle.height = `${newHeight}px`; 180 | }, i * speedMultiplier); 181 | timeoutsRef.current.push(timeoutId); // Store timeout 182 | } 183 | } 184 | }; 185 | 186 | const clearTimeouts = () => { 187 | timeoutsRef.current.forEach(timeoutId => clearTimeout(timeoutId)); // Clear all timeouts 188 | timeoutsRef.current = []; // Reset the array 189 | }; 190 | 191 | return ( 192 |
193 |

194 | Sorting Visualizer 195 |

196 |
197 | 205 | {/* Dropdown for selecting sorting algorithm */} 206 | 219 | {/* Dropdown for selecting playback speed */} 220 | 229 | {/* Submit button to execute the selected sort */} 230 | 239 |
240 |
241 | {array.map((value, idx) => ( 242 |
250 | ))} 251 |
252 |
253 | ); 254 | }; 255 | 256 | function randomIntFromInterval(min, max) { 257 | return Math.floor(Math.random() * (max - min + 1) + min); 258 | } 259 | 260 | export default SortingVisualizer; -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | ReactDOM.render( 6 | , 7 | document.getElementById("root") 8 | ); -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | } 4 | 5 | .container { 6 | max-w-md mx-auto p-4 mt-4 bg-gray-100 rounded-lg shadow-md; 7 | } 8 | 9 | .bar { 10 | float: left; 11 | width: 20px; 12 | height: 100px; 13 | margin: 0 2px; 14 | background-color: #4CAF50; 15 | } 16 | 17 | .bar:hover { 18 | background-color: #3e8e41; 19 | } 20 | 21 | #array-size { 22 | display: flex; 23 | align-items: center; 24 | margin-bottom: 20px; 25 | } 26 | 27 | #array-size span { 28 | margin-left: 10px; 29 | } 30 | 31 | #array-size input[type="range"] { 32 | -webkit-appearance: none; 33 | width: 100%; 34 | height: 10px; 35 | border-radius: 5px; 36 | background: #d3d3d3; 37 | outline: none; 38 | padding: 0; 39 | margin: 0 10px; 40 | } 41 | 42 | #array-size input[type="range"]::-webkit-slider-thumb { 43 | -webkit-appearance: none; 44 | width: 20px; 45 | height: 20px; 46 | border-radius: 50%; 47 | background: #4CAF50; 48 | cursor: pointer; 49 | margin-top: -5px; 50 | } 51 | 52 | #chart { 53 | height: 400px; 54 | width: 100%; 55 | background-color: #fff; 56 | border: 1px solid #ddd; 57 | padding: 20px; 58 | margin-top: 20px; 59 | } 60 | 61 | #chart canvas { 62 | height: 100%; 63 | width: 100%; 64 | } -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [ 3 | "./src/**/*.{js,jsx,ts,tsx}", 4 | ], 5 | theme: { 6 | extend: {}, 7 | }, 8 | plugins: [], 9 | } --------------------------------------------------------------------------------