Skip to content

Commit a5c73ae

Browse files
committed
Import from CodePen
Imported from MrTimcakes's CodePen https://codepen.io/MrTimcakes/pen/xMKQBQ
1 parent 5385c0a commit a5c73ae

3 files changed

Lines changed: 251 additions & 0 deletions

File tree

Algorithm-Visualiser.js

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
class algorithmVisualiser{
2+
configVariables(){ // Bodge for ES6 not supporting Public Field Declarations
3+
self.dataSet = {
4+
data: [],
5+
comp: [],
6+
compCount: 0,
7+
swap: [],
8+
swapCount: 0,
9+
iteration: 0
10+
};
11+
self.options = {
12+
size: 128,
13+
delay: 16,
14+
structure: "Random",
15+
algorithm: "Bubble Sort",
16+
gui: false,
17+
fps: false,
18+
lineColor: "#0000FF",
19+
compColor: "#FF0000",
20+
swapColor: "#00FF00",
21+
lineWidth: 2.5
22+
};
23+
}
24+
constructor(canvas, opts = {}) {
25+
self = this;
26+
self.configVariables();
27+
self.configDynamicFunctions();
28+
self.options = {...self.options, ...opts}; // Apply construction options
29+
self.ctx = canvas.getContext('2d');
30+
31+
if(self.options.gui){
32+
var gui = new dat.GUI();
33+
gui.add(self.options, 'algorithm', self.algorithms.map(x => x.name)).name("Algorithm");
34+
gui.add(self.options, 'structure', self.structures.map(x => x.name)).name("Structure").onFinishChange(self.reset);
35+
gui.add(self.options, 'size', 64, 1024, 64).name("Size").onChange(self.reset);
36+
gui.add(self.options, 'delay', 0, 64).name("Delay (ms)");
37+
gui.add(self, 'reset').name("Reset");
38+
gui.add(self, 'startStop').name("Start / Stop");
39+
40+
let advFolder = gui.addFolder("Advanced");
41+
advFolder.addColor(self.options, 'lineColor');
42+
advFolder.addColor(self.options, 'compColor');
43+
advFolder.addColor(self.options, 'swapColor');
44+
advFolder.add(self.options, 'lineWidth', 0, 15);
45+
46+
if(self.options.fps != false && self.options.gui){
47+
self.options.fps = new Stats();
48+
self.options.fps.domElement.height = '48px';
49+
[].forEach.call(self.options.fps.domElement.children, (child) => (child.style.display = ''));
50+
51+
var perfFolder = gui.addFolder("Performance");
52+
var perfLi = document.createElement("li");
53+
self.options.fps.domElement.style.position = "static";
54+
perfLi.appendChild(self.options.fps.domElement);
55+
perfLi.classList.add("gui-stats");
56+
perfFolder.__ul.appendChild(perfLi);
57+
}
58+
59+
}
60+
61+
if(self.options.fps != false && !(self.options.gui)){
62+
self.options.fps = new Stats();
63+
self.options.fps.showPanel( 0 ); // 0: fps, 1: ms, 2: mb, 3+: custom
64+
self.options.fps.domElement.style = "position:fixed;top:0;left:0;";
65+
document.body.appendChild( self.options.fps.domElement );
66+
}
67+
68+
self.reset();
69+
self.draw();
70+
}
71+
72+
startStop(){
73+
self.algorithms.find(x => x.name === self.options.algorithm).algorithm();
74+
}
75+
76+
reset(){
77+
self.structures.find(x => x.name === self.options.structure).func();
78+
self.dataSet.iteration = 0;
79+
self.dataSet.swapCount = 0;
80+
self.dataSet.compCount = 0;
81+
}
82+
83+
draw(){
84+
if(self.options.fps != false){self.options.fps.begin();}
85+
var ctx = self.ctx;
86+
var width = ctx.canvas.width = ctx.canvas.clientWidth;
87+
var height = ctx.canvas.height = ctx.canvas.clientHeight;
88+
var scaleY = (height / Math.max(...self.dataSet.data));
89+
var lineWidth = self.options.lineWidth || (width/self.dataSet.data.length)+0.5;
90+
91+
92+
for(let i=0;i<self.dataSet.data.length;i++){ // For every element in the dataset, draw its line
93+
ctx.strokeStyle = self.options.lineColor;
94+
ctx.lineWidth = lineWidth;
95+
ctx.beginPath();
96+
97+
if(self.dataSet.comp.includes(i)){ // If the line is currently in a comparison, draw it in that colour
98+
ctx.lineWidth = lineWidth*3;
99+
ctx.strokeStyle = self.options.compColor;
100+
}
101+
102+
if(self.dataSet.swap.includes(i)){ // If the line is currently in a swap, draw it in that colour
103+
ctx.lineWidth = lineWidth*3;
104+
ctx.strokeStyle = self.options.swapColor;
105+
}
106+
107+
ctx.moveTo(i*(width/self.dataSet.data.length), height);
108+
ctx.lineTo(i*(width/self.dataSet.data.length), height - (self.dataSet.data[i] * scaleY));
109+
110+
ctx.stroke();
111+
}
112+
113+
ctx.font = "15px Arial";
114+
var textStr = `${self.options.algorithm} Iteration: ${self.dataSet.iteration} Comparisons: ${self.dataSet.compCount} Swaps: ${self.dataSet.swapCount}`;
115+
ctx.fillText(textStr, 10, 25);
116+
117+
if(self.options.fps != false){self.options.fps.end();}
118+
window.requestAnimationFrame( () => self.draw() );
119+
//window.requestAnimationFrame( this.draw.bind(this) ); // Fix "this" binding
120+
}
121+
122+
async wait(time) {
123+
return new Promise(function(resolve) {
124+
setTimeout(resolve, time);
125+
});
126+
}
127+
128+
configDynamicFunctions(){ // Bodge for ES6 not supporting Public Field Declarations
129+
self.structures = [
130+
{name:"Random", func: ()=>{
131+
self.dataSet.data = Array.from({length: self.options.size}, () => Math.floor(Math.random() * self.options.size));
132+
}},
133+
{name:"Random Unique", func: ()=>{
134+
self.dataSet.data = Array.from(new Array(self.options.size),(v,i)=>i);
135+
for (let i = self.dataSet.data.length - 1; i > 0; i--) {
136+
const j = Math.floor(Math.random() * (i + 1));
137+
[self.dataSet.data[i], self.dataSet.data[j]] = [self.dataSet.data[j], self.dataSet.data[i]];
138+
}
139+
}},
140+
{name:"Reverse Order", func: ()=>{
141+
self.dataSet.data = Array.from(new Array(self.options.size),(v,i)=>self.options.size-i);
142+
}},
143+
{name:"Nearly Sorted", func: ()=>{
144+
// TODO: Make Nearly Sorted Arr
145+
}},
146+
{name:"Few Unique", func: ()=>{
147+
// TODO: Make Arr with a few unique values
148+
}}
149+
]
150+
151+
self.algorithms = [
152+
153+
{name:"Bubble Sort",algorithm: async ()=>{
154+
for (let i = ( self.dataSet.data.length - 1); i >= 0; i--){
155+
for (let j = ( self.dataSet.data.length - i); j > 0; j--){
156+
self.dataSet.iteration++;
157+
self.dataSet.compCount++;
158+
self.dataSet.swap = [];
159+
self.dataSet.comp = [j-1, j];
160+
if (self.dataSet.data[j] < self.dataSet.data[j - 1]){
161+
[ self.dataSet.data[j-1], self.dataSet.data[j] ] = [ self.dataSet.data[j], self.dataSet.data[j-1] ];
162+
self.dataSet.swap = [j-1, j];
163+
self.dataSet.swapCount++;
164+
}
165+
await self.wait(self.options.delay); // Delay before next iteration
166+
}
167+
}
168+
self.dataSet.swap = []; // Finished Empty active data
169+
self.dataSet.comp = []; // Finished Empty active data
170+
}},
171+
172+
{name:"Quick Sort",algorithm: async ()=>{
173+
174+
}},
175+
176+
{name:"Reverse Order",algorithm: async ()=>{
177+
178+
}}
179+
180+
];
181+
}
182+
}
183+
184+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
185+
/* End Class */
186+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
187+
188+
var AV;
189+
190+
window.onload = () => {
191+
let AVCanvas = document.getElementById("algorithm-visualiser");
192+
AV = new algorithmVisualiser(AVCanvas, {gui: true, fps: true});
193+
};

css/style.css

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*///////////////////////////////////////////////////////
2+
Colours
3+
////////////////////////////////////////////////////////*/
4+
/*///////////////////////////////////////////////////////
5+
Variables
6+
////////////////////////////////////////////////////////*/
7+
/*///////////////////////////////////////////////////////
8+
YO Stuff
9+
////////////////////////////////////////////////////////*/
10+
#algorithm-visualiser {
11+
width: 100vw;
12+
height: 100vh;
13+
display: block;
14+
}
15+
16+
.dg li.gui-stats:not(.folder) {
17+
height: auto;
18+
}
19+
20+
/*///////////////////////////////////////////////////////
21+
General
22+
////////////////////////////////////////////////////////*/
23+
html {
24+
font-family: 'Roboto', sans-serif;
25+
font-size: 62.5%;
26+
height: 100%;
27+
}
28+
29+
body {
30+
margin: 0 auto;
31+
font-size: 1.6rem;
32+
width: 100%;
33+
height: 100%;
34+
margin: 0 auto;
35+
display: flex;
36+
align-items: center;
37+
justify-content: center;
38+
}

index.html

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<!DOCTYPE html>
2+
<html lang="en" >
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<title>Algorithm Visualiser</title>
7+
<meta name="viewport" content="width=device-width, initial-scale=1">
8+
<link href="https://fonts.googleapis.com/css?family=Roboto:100,200,300,400" rel="stylesheet">
9+
<link rel="stylesheet" href="css/style.css">
10+
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script> -->
11+
</head>
12+
13+
<body>
14+
<canvas id="algorithm-visualiser"></canvas>
15+
<script src='https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.5/dat.gui.min.js'></script>
16+
<script src='https://cdnjs.cloudflare.com/ajax/libs/stats.js/r16/Stats.min.js'></script>
17+
<script src="Algorithm-Visualiser.js"></script>
18+
</body>
19+
20+
</html>

0 commit comments

Comments
 (0)