├── .gitignore
├── .vscode
└── launch.json
├── README.md
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
└── robots.txt
└── src
├── App.css
├── App.js
├── App.test.js
├── controller
└── SortingVisualizer.jsx
├── index.css
├── index.js
├── logo.svg
├── model
├── BubbleSort.js
├── HeapSort.js
├── HelperFunctions.js
├── InsertionSort.js
├── MergeSort.js
└── QuickSort.js
├── reportWebVitals.js
├── setupTests.js
└── view
└── SortingVisualizer.css
/.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 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "pwa-chrome",
9 | "request": "launch",
10 | "name": "Launch Chrome against localhost",
11 | "url": "http://localhost:8080",
12 | "webRoot": "${workspaceFolder}"
13 | }
14 | ]
15 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Welcome to my sorting visualizer! This project implement a beautiful visualizer for 5 of my favorite sorting algorithms using React framework. You can find all important code in src/model, view, controller.
2 |
3 | # To access the visualizer
4 | http://xuqianzhi.github.io/Sorting-Visualizer.
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sorting_visualizer",
3 | "homepage": "http://xuqianzhi.github.io/Sorting-Visualizer",
4 | "version": "0.1.0",
5 | "private": true,
6 | "dependencies": {
7 | "@testing-library/jest-dom": "^5.12.0",
8 | "@testing-library/react": "^11.2.7",
9 | "@testing-library/user-event": "^12.8.3",
10 | "react": "^17.0.2",
11 | "react-dom": "^17.0.2",
12 | "react-scripts": "4.0.3",
13 | "web-vitals": "^1.1.2"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test",
19 | "eject": "react-scripts eject",
20 | "predeploy": "npm run build",
21 | "deploy": "gh-pages -d build"
22 | },
23 | "eslintConfig": {
24 | "extends": [
25 | "react-app",
26 | "react-app/jest"
27 | ]
28 | },
29 | "browserslist": {
30 | "production": [
31 | ">0.2%",
32 | "not dead",
33 | "not op_mini all"
34 | ],
35 | "development": [
36 | "last 1 chrome version",
37 | "last 1 firefox version",
38 | "last 1 safari version"
39 | ]
40 | },
41 | "devDependencies": {
42 | "gh-pages": "^3.2.0"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xuqianzhi/Sorting-Visualizer/c140cac54a21338d7a2e671f2e767d8a676b1364/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/xuqianzhi/Sorting-Visualizer/c140cac54a21338d7a2e671f2e767d8a676b1364/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xuqianzhi/Sorting-Visualizer/c140cac54a21338d7a2e671f2e767d8a676b1364/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": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
10 | @media (prefers-reduced-motion: no-preference) {
11 | .App-logo {
12 | animation: App-logo-spin infinite 20s linear;
13 | }
14 | }
15 |
16 | .App-header {
17 | background-color: #282c34;
18 | min-height: 100vh;
19 | display: flex;
20 | flex-direction: column;
21 | align-items: center;
22 | justify-content: center;
23 | font-size: calc(10px + 2vmin);
24 | color: white;
25 | }
26 |
27 | .App-link {
28 | color: #61dafb;
29 | }
30 |
31 | @keyframes App-logo-spin {
32 | from {
33 | transform: rotate(0deg);
34 | }
35 | to {
36 | transform: rotate(360deg);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import logo from './logo.svg';
2 | import './App.css';
3 | import SortingVisualizer from './controller/SortingVisualizer'
4 |
5 | function App() {
6 | return (
7 |
8 |
9 |
10 | );
11 | }
12 |
13 | export default App;
14 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/src/controller/SortingVisualizer.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, useState } from 'react';
2 | import reactDom from 'react-dom';
3 | import '../view/SortingVisualizer.css'
4 | import {mergeSortAnimation} from '../model/MergeSort.js'
5 | import {quickSortAnimation} from '../model/QuickSort.js'
6 | import {insertionSortAnimation} from '../model/InsertionSort.js'
7 | import {bubbleSortAnimation} from '../model/BubbleSort.js'
8 | import {heapSortAnimation} from '../model/HeapSort.js'
9 | import {randomIntFromInterval, isArraySorted} from '../model/HelperFunctions.js'
10 |
11 | const ANIMATION_LAG = 50;
12 | const MAIN_BAR_COLOR = "rgb(187, 145, 248)";
13 | const CHANGING_BAR_COLOR = "rgb(252, 242, 81)"
14 | const SUPPORTING_COLOR = "darkorange"
15 | const FINISHED_COLOR = "mediumaquamarine"
16 | const RUNNING_STATUS = Object.freeze({'running': 'running', 'pausing': 'pausing', 'not_running': 'not_running'})
17 |
18 | export default class SortingVisualizer extends Component {
19 |
20 | constructor(props) {
21 | super(props);
22 | this.state = {
23 | bars: [],
24 | bar_num: 100, //determines bar num, min: 8, max: 200;
25 | running_status: RUNNING_STATUS.not_running,
26 | animation_speed: 2,
27 | timeoutIDs: [], //for clearTimeout
28 | bar_change_animation: [], //for pausing and continuing animation
29 | animation_begin_at: 0 //for pausing and continuing animation
30 | }
31 | }
32 |
33 | componentDidMount() {
34 | this.resetBars();
35 | }
36 |
37 | resetBars() {
38 | const buf = [];
39 | const curr_displayed_bars = document.getElementsByClassName("bar");
40 | for (let i = 0; i < this.state.bar_num; i++) {
41 | buf.push(randomIntFromInterval(5, 300));
42 | if (curr_displayed_bars[i]) {
43 | curr_displayed_bars[i].style.backgroundColor = MAIN_BAR_COLOR;
44 | }
45 | }
46 | this.setState({bars: buf});
47 | }
48 |
49 | displayAnimation(bar_change_animation, animation_begin_at) {
50 | const curr_displayed_bars = document.getElementsByClassName("bar");
51 | const animation_speed = 1 / (2**(this.state.animation_speed));
52 | var timeoutIDs = [];
53 | for (let i = animation_begin_at; i < bar_change_animation.length; i++) {
54 | const changing_bar_index = bar_change_animation[i][0];
55 | const new_bar_height = bar_change_animation[i][1];
56 | let supporting_index = null;
57 | if (bar_change_animation[i].length == 3) {
58 | supporting_index = bar_change_animation[i][2];
59 | }
60 |
61 | // begin animation
62 | const currTimeoutID = setTimeout(() => {
63 | this.setState({running_status: RUNNING_STATUS.running});
64 | if (i != 0) {
65 | const prev_changing_bar_index = bar_change_animation[i - 1][0];
66 | curr_displayed_bars[prev_changing_bar_index].style.backgroundColor = MAIN_BAR_COLOR;
67 | if (bar_change_animation[i - 1].length == 3) {
68 | const prev_supporting_index = bar_change_animation[i - 1][2];
69 | curr_displayed_bars[prev_supporting_index].style.backgroundColor = MAIN_BAR_COLOR;
70 | }
71 | }
72 | let buf = this.state.bars;
73 | buf[changing_bar_index] = new_bar_height;
74 | this.setState({bars: buf});
75 | if (supporting_index) { // supporting index exist
76 | curr_displayed_bars[supporting_index].style.backgroundColor = SUPPORTING_COLOR;
77 | }
78 | curr_displayed_bars[changing_bar_index].style.backgroundColor = CHANGING_BAR_COLOR;
79 | this.setState({animation_begin_at: i}) // keep track of current index for pausing and continuing
80 | }, ANIMATION_LAG * animation_speed * (i - animation_begin_at));
81 | timeoutIDs.push(currTimeoutID);
82 | }
83 |
84 | // animation stopped
85 | const id1 = setTimeout(() => {
86 | this.setState({animation_begin_at: 0, bar_change_animation: []});
87 | document.getElementById('pause-button').style.display = 'none';
88 | document.getElementById('stop-button').style.display = 'none';
89 | for (let i = 0; i < this.state.bar_num; i++) {
90 | curr_displayed_bars[i].style.backgroundColor = FINISHED_COLOR;
91 | }
92 | }, ANIMATION_LAG * animation_speed * (bar_change_animation.length - animation_begin_at));
93 | const id2 = setTimeout(() => {
94 | this.setState({running_status: RUNNING_STATUS.not_running});
95 | }, ANIMATION_LAG * animation_speed * (bar_change_animation.length - animation_begin_at) + 1000);
96 | const id3 = setTimeout(() => {
97 | this.resetBars();
98 | }, ANIMATION_LAG * animation_speed * (bar_change_animation.length - animation_begin_at) + 1500);
99 |
100 | timeoutIDs.push(id1);
101 | timeoutIDs.push(id2);
102 | timeoutIDs.push(id3);
103 | this.setState({timeoutIDs, timeoutIDs});
104 | }
105 |
106 | sortAndDisplay(method) {
107 | /*
108 | method type: String
109 | method cases: merge, quick, insertion, bubble, heap
110 | */
111 | const lst = this.state.bars.slice();
112 | var bar_change_animation = [];
113 | if (method === "merge") {
114 | mergeSortAnimation(lst, 0, lst.length, bar_change_animation);
115 | } else if (method === "quick") {
116 | quickSortAnimation(lst, 0, lst.length, bar_change_animation);
117 | } else if (method === "insertion") {
118 | insertionSortAnimation(lst, bar_change_animation);
119 | } else if (method === "bubble") {
120 | bubbleSortAnimation(lst, bar_change_animation);
121 | } else if (method === "heap") {
122 | heapSortAnimation(lst, bar_change_animation);
123 | } else {
124 | return;
125 | }
126 | this.displayAnimation(bar_change_animation, 0);
127 | this.setState({bar_change_animation: bar_change_animation});
128 | }
129 |
130 | barNumSliderChanged(event) {
131 | const val = parseInt(event.target.value);
132 | const buf = [];
133 | const curr_displayed_bars = document.getElementsByClassName("bar");
134 | for (let i = 0; i < val; i++) {
135 | buf.push(randomIntFromInterval(5, 300));
136 | if (curr_displayed_bars[i]) {
137 | curr_displayed_bars[i].style.backgroundColor = MAIN_BAR_COLOR;
138 | }
139 | }
140 | this.setState({bar_num: val, bars: buf});
141 | }
142 |
143 | speedSliderChanged(event) {
144 | const val = parseInt(event.target.value);
145 | this.setState({animation_speed: val});
146 | }
147 |
148 | clearOngoingAnimation() {
149 | const timeoutIDs = this.state.timeoutIDs;
150 | if (timeoutIDs.length == 0) {
151 | return;
152 | }
153 | for (let i = 0; i < timeoutIDs.length; i++) {
154 | const currID = timeoutIDs[i];
155 | clearTimeout(currID);
156 | }
157 | }
158 |
159 | pauseAnimation() {
160 | this.clearOngoingAnimation();
161 | this.setState({running_status: RUNNING_STATUS.pausing, timeoutIDs: []});
162 | }
163 |
164 | continueAnimation() {
165 | this.displayAnimation(this.state.bar_change_animation, this.state.animation_begin_at);
166 | }
167 |
168 | stopAndResetAnimation() {
169 | this.clearOngoingAnimation();
170 | this.setState({timeoutIDs: [], bar_change_animation: [], animation_begin_at: 0, running_status: RUNNING_STATUS.not_running});
171 | this.resetBars();
172 | }
173 |
174 | render() {
175 | const bars = this.state.bars.slice();
176 | const bar_num = this.state.bar_num;
177 | const running_status = this.state.running_status;
178 | const initial_button_position = 7;
179 | const animation_speed = 2**(this.state.animation_speed);
180 | return (
181 |
182 |
183 |
185 |
187 |
189 |
191 |
193 |
194 |
195 | this.speedSliderChanged(event)}/>
203 |
204 |
205 |
211 |
212 |
217 |
223 |
224 |
225 |
226 |
227 | {bars.map((val, idx) => (
228 |
237 |
238 | ))}
239 |
this.barNumSliderChanged(event)}/>
247 |
248 |
249 | )
250 | }
251 | }
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
18 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/model/BubbleSort.js:
--------------------------------------------------------------------------------
1 | export function bubbleSortAnimation(
2 | lst,
3 | bar_change_animation) {
4 | /*
5 | lst: is the entire whole array
6 | bar_change_animation: keeps track all intermediate changes,
7 | 0th place is new index, 1st place is new height
8 | */
9 | if (lst.length <= 1) {
10 | return;
11 | }
12 | while (true) {
13 | var somethingSwapped = false;
14 | for (let i = 0; i < lst.length - 1; i++) {
15 | const firstElem = lst[i];
16 | const secondElem = lst[i + 1];
17 | if (firstElem > secondElem) {
18 | lst[i] = secondElem;
19 | lst[i + 1] = firstElem;
20 | somethingSwapped = true;
21 | bar_change_animation.push([i, secondElem]);
22 | bar_change_animation.push([i + 1, firstElem]);
23 | }
24 | }
25 | if (!somethingSwapped) {
26 | break;
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/src/model/HeapSort.js:
--------------------------------------------------------------------------------
1 | export function heapSortAnimation(
2 | lst,
3 | bar_change_animation) {
4 | /*
5 | lst: is the entire whole array
6 | start & end: the segment of the array we are sorting
7 | bar_change_animation: keeps track all intermediate changes,
8 | 0th place is new index, 1st place is new height
9 | */
10 | var heap = new MinHeap();
11 | for (let i = 0; i < lst.length; i++) {
12 | bar_change_animation.push([i, lst[i]]);
13 | heap.insert(lst[i]);
14 | }
15 | for (let i = 0; i < lst.length; i++) {
16 | const min_val = heap.extractMin();
17 | lst[i] = min_val;
18 | bar_change_animation.push([i, min_val]);
19 | }
20 | }
21 |
22 |
23 | function MinHeap() {
24 | this.data = [];
25 | }
26 |
27 | MinHeap.prototype.insert = function(val) {
28 | this.data.push(val);
29 | this.bubbleUp(this.data.length-1);
30 | };
31 |
32 | MinHeap.prototype.bubbleUp = function(index) {
33 | while (index > 0) {
34 | // get the parent
35 | var parent = Math.floor((index + 1) / 2) - 1;
36 |
37 | // if parent is greater than child
38 | if (this.data[parent] > this.data[index]) {
39 | // swap
40 | var temp = this.data[parent];
41 | this.data[parent] = this.data[index];
42 | this.data[index] = temp;
43 | }
44 |
45 | index = parent;
46 | }
47 | };
48 |
49 | MinHeap.prototype.extractMin = function() {
50 | var min = this.data[0];
51 |
52 | // set first element to last element
53 | this.data[0] = this.data.pop();
54 |
55 | // call bubble down
56 | this.bubbleDown(0);
57 |
58 | return min;
59 | };
60 |
61 | MinHeap.prototype.bubbleDown = function(index) {
62 | while (true) {
63 | var child = (index+1)*2;
64 | var sibling = child - 1;
65 | var toSwap = null;
66 |
67 | // if current is greater than child
68 | if (this.data[index] > this.data[child]) {
69 | toSwap = child;
70 | }
71 |
72 | // if sibling is smaller than child, but also smaller than current
73 | if (this.data[index] > this.data[sibling] && (this.data[child] == null || (this.data[child] !== null && this.data[sibling] < this.data[child]))) {
74 | toSwap = sibling;
75 | }
76 |
77 | // if we don't need to swap, then break.
78 | if (toSwap == null) {
79 | break;
80 | }
81 |
82 | var temp = this.data[toSwap];
83 | this.data[toSwap] = this.data[index];
84 | this.data[index] = temp;
85 |
86 | index = toSwap;
87 | }
88 |
89 | MinHeap.prototype.clear = function() {
90 | this.data = [];
91 | }
92 | };
93 |
--------------------------------------------------------------------------------
/src/model/HelperFunctions.js:
--------------------------------------------------------------------------------
1 | export function randomIntFromInterval(min, max) {
2 | // min and max included
3 | return Math.floor(Math.random() * (max - min + 1) + min);
4 | }
5 |
6 | export function isArraySorted(lst) {
7 | let prev_val = Number.MIN_SAFE_INTEGER;
8 | for (let i = 0; i < lst.length; i++) {
9 | const curr_val = lst[i];
10 | if (curr_val < prev_val) {
11 | return false;
12 | }
13 | prev_val = curr_val;
14 | }
15 | return true;
16 | }
--------------------------------------------------------------------------------
/src/model/InsertionSort.js:
--------------------------------------------------------------------------------
1 | export function insertionSortAnimation(
2 | lst,
3 | bar_change_animation) {
4 | /*
5 | lst: is the entire whole array
6 | start & end: the segment of the array we are sorting
7 | bar_change_animation: keeps track all intermediate changes,
8 | 0th place is new index, 1st place is new height
9 | */
10 | for (let i = 1; i < lst.length; i++) {
11 | const inserting_elem = lst[i];
12 | var j = i - 1; // j will be the inserting position
13 | var temp_animations = [];
14 | while(j >= 0 && inserting_elem < lst[j]) {
15 | lst[j + 1] = lst[j];
16 | temp_animations.push([j + 1, lst[j]]);
17 | temp_animations.push([j, inserting_elem]);
18 | j -= 1;
19 | }
20 | for (let k = 0; k < temp_animations.length; k++) {
21 | var curr_animation = temp_animations[k];
22 | curr_animation.push([j + 1]);
23 | bar_change_animation.push(curr_animation);
24 | }
25 | lst[j + 1] = inserting_elem;
26 | bar_change_animation.push([j + 1, lst[j + 1], j + 1]);
27 | }
28 | }
--------------------------------------------------------------------------------
/src/model/MergeSort.js:
--------------------------------------------------------------------------------
1 | export function mergeSortAnimation(
2 | lst,
3 | start,
4 | end,
5 | bar_change_animation) {
6 | /*
7 | lst: is the entire whole array
8 | start & end: the segment of the array we are sorting
9 | bar_change_animation: keeps track all intermediate changes,
10 | 0th place is new index, 1st place is new height
11 | */
12 |
13 | if (start + 1 >= end) {
14 | return;
15 | }
16 | const mid = Math.floor((start + end) / 2);
17 | mergeSortAnimation(lst, start, mid, bar_change_animation);
18 | mergeSortAnimation(lst, mid, end, bar_change_animation);
19 | let i = start; //left half index
20 | let j = mid; //right half index
21 | let k = start; //result list index
22 | let result = lst.slice();
23 | while (i < mid || j < end) {
24 | let temp;
25 | if (i === mid) {
26 | while (k < end) {
27 | bar_change_animation.push([k, lst[j], j]);
28 | result[k] = lst[j];
29 | k += 1;
30 | j += 1;
31 | }
32 | break;
33 | }
34 | if (j === end) {
35 | while (k < end) {
36 | bar_change_animation.push([k, lst[i], i]);
37 | result[k] = lst[i];
38 | k += 1;
39 | i += 1;
40 | }
41 | break;
42 | }
43 | const leftElem = lst[i];
44 | const rightElem = lst[j];
45 | temp = result[k];
46 | if (leftElem < rightElem) {
47 | bar_change_animation.push([k, leftElem, j]);
48 | result[k] = leftElem;
49 | i += 1;
50 | } else {
51 | bar_change_animation.push([k, rightElem, i]);
52 | result[k] = rightElem;
53 | j += 1;
54 | }
55 | k += 1;
56 | }
57 | for (let i = start; i < end; i++) {
58 | lst[i] = result[i];
59 | }
60 | }
--------------------------------------------------------------------------------
/src/model/QuickSort.js:
--------------------------------------------------------------------------------
1 | export function quickSortAnimation(
2 | lst,
3 | start,
4 | end,
5 | bar_change_animation) {
6 | /*
7 | This function pick the median of start and end index as the pivot
8 | lst: is the entire whole array
9 | start & end: the segment of the array we are sorting
10 | bar_change_animation: keeps track all intermediate changes,
11 | 0th: new index, 1st: new height, 2nd: pivot index
12 | */
13 | if (start + 1 >= end) {
14 | return;
15 | }
16 | const pivot_index = randomIntFromInterval(start, end - 1);
17 | // const mid = Math.floor((start + end) / 2);
18 |
19 | const pivot_val = lst[pivot_index];
20 | let leftHalf = [];
21 | let rightHalf = [];
22 | for (let i = start; i < end; i++) {
23 | if (i == pivot_index) {continue}
24 | const curr_val = lst[i];
25 | curr_val <= pivot_val ? leftHalf.push(curr_val) : rightHalf.push(curr_val);
26 | }
27 | const new_pivot_index = leftHalf.length + start;
28 | leftHalf.push(pivot_val);
29 | const overall_list = leftHalf.concat(rightHalf);
30 | for (let i = start; i < end; i++) {
31 | lst[i] = overall_list[i - start];
32 | bar_change_animation.push([i, lst[i], new_pivot_index]);
33 | }
34 | quickSortAnimation(lst, start, new_pivot_index, bar_change_animation);
35 | quickSortAnimation(lst, new_pivot_index + 1, end, bar_change_animation);
36 | }
37 |
38 | function randomIntFromInterval(min, max) {
39 | // min and max included
40 | return Math.floor(Math.random() * (max - min + 1) + min);
41 | }
--------------------------------------------------------------------------------
/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/src/view/SortingVisualizer.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: rgb(30, 30, 30);
3 | }
4 |
5 | .button-container {
6 | background-color: rgb(61, 61, 61);
7 | height: 120px;
8 | position: relative;
9 | }
10 |
11 | .button {
12 | background-color: rgb(92, 91, 90);
13 | padding: 10px;
14 | margin: 10px;
15 | margin-top: 50px;
16 | width: 12%;
17 | border-width: 1px;
18 | border-radius: 12px;
19 | position: absolute;
20 |
21 | color: white;
22 | font-size: 16px;
23 | font-weight: bold;
24 | }
25 |
26 | .bar-container {
27 | margin: auto;
28 | width: 1000px;
29 | height: 430px;
30 | position: relative;
31 | background-color: transparent;
32 | }
33 |
34 | .bar {
35 | position: absolute;
36 | bottom: 0px;
37 | margin: 0 1px;
38 | border-top-left-radius: 10px;
39 | border-top-right-radius: 10px;
40 | border-bottom-left-radius: 6px;
41 | border-bottom-right-radius: 6px;
42 | }
43 |
44 | .side-container {
45 | width: 12%;
46 | height: 75px;
47 | position: absolute;
48 | right: 20px;
49 | margin-top: 35px;
50 | /* background-color: yellow; */
51 | background-color:rgb(61, 61, 61);
52 | }
53 |
54 | .barnum-slider {
55 | margin-top: 50%;
56 | -webkit-appearance: none;
57 | height: 10px;
58 | width: 90%;
59 | background: #d3d3d3;
60 | opacity: 0.7;
61 | border-radius: 15px;
62 | }
63 |
64 | .barnum-slider::-webkit-slider-thumb {
65 | border-radius: 13px;
66 | appearance: none;
67 | width: 26px;
68 | height: 26px;
69 | background: rgb(187, 145, 248);
70 | }
71 |
72 | .speed-slider {
73 | margin-top: 27%;
74 | position: absolute;
75 | -webkit-appearance: none;
76 | height: 7px;
77 | width: 90%;
78 | background:white;
79 | opacity: 0.7;
80 | border-radius: 15px;
81 | }
82 |
83 | .speed-slider::-webkit-slider-thumb {
84 | border-radius: 9px;
85 | appearance: none;
86 | width: 18px;
87 | height: 18px;
88 | background: lightgrey;
89 | }
--------------------------------------------------------------------------------