commit b58d52529b6e394a7944469c48c721c764873bd5 Author: Benjamin Kraft Date: Sun Mar 26 23:52:45 2023 +0200 Init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6ef218 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea + diff --git a/project.json b/project.json new file mode 100644 index 0000000..0ad7b23 --- /dev/null +++ b/project.json @@ -0,0 +1,6 @@ +{ + "display_name": "Pathfinder", + "info_text": "A graphical demonstration of some pathfinder algorithms", + "visible": true, + "tags": ["Simulation", "Tool"] +} \ No newline at end of file diff --git a/public/data/images/favicon.ico b/public/data/images/favicon.ico new file mode 100644 index 0000000..3172667 Binary files /dev/null and b/public/data/images/favicon.ico differ diff --git a/public/data/scripts/algorithm.js b/public/data/scripts/algorithm.js new file mode 100644 index 0000000..5a2a3cd --- /dev/null +++ b/public/data/scripts/algorithm.js @@ -0,0 +1,321 @@ +class Algorithm{ + + constructor(){ + this.startNode = game.startNode; + this.targetNode = game.targetNode; + + this.grid = game.grid; + + this.startDate = new Date(); + this.timeDiff = 0; + } + + get running(){ + return !this.hasSucceeded && !this.hasFailed; + } + + display(){ + push(); + scale(1 / this.grid.nodeSize.x, 1 / this.grid.nodeSize.y); + fill(0); + textSize(20); + textAlign(CENTER, TOP); + text('Time: ' + this.timeDiff / 1000 + 's', this.grid.size / 2, -this.grid.nodeSize.y / 2 - 50); + pop(); + } + + update(){ + this.timeDiff = new Date().getTime() - this.startDate.getTime(); + } + + success(){ + console.log('Success!'); + this.hasSucceeded = true; + } + + failed(){ + console.log('Failed!'); + this.hasFailed = true; + } + +} + +function startAlgorithm(){ + switch ($('#algorithm_type').val()){ + case 'astar': + game.start(new AStar()); + break; + case 'dijkstra': + game.start(new Dijkstra()); + break; + case 'dstar': + game.start(new DStar()); + break; + } +} + + +class DStar extends Algorithm{ + + constructor() { + super(); + + this.openList = [] + } + + +} + + +class AStar extends Algorithm{ + + constructor(){ + super(); + + this.startNode.f = 0; + this.startNode.g = 0; + + this.openList = [this.startNode]; + this.closedList = []; + } + + get minFNodeIndex(){ + let f = this.openList[0].f, index = 0; + this.openList.forEach((n, i) => { + if (n.f < f){ + f = n.f; + index = i; + } + }); + return index; + } + + update(){ + if (!this.running){ + return; + } + + super.update(); + + if (!this.openList.length){ + this.failed(); + } else { + let index = this.minFNodeIndex; + let currentNode = this.openList[index]; + this.openList.splice(index, 1); + + if (currentNode === this.targetNode){ + this.success(); + } else { + this.closedList.push(currentNode); + + currentNode.successors.forEach(successor => { + + if (this.closedList.find(n => n === successor)){ + return; + } + + let provisionalG = currentNode.g + currentNode.pos.dist(successor.pos); + if (this.openList.find(n => n === successor) && provisionalG >= successor.g){ + return; + } + + successor.predecessor = currentNode + successor.g = provisionalG; + + successor.f = provisionalG + successor.distanceToTarget; + if (!this.openList.find(n => n === successor)){ + this.openList.push(successor); + } + }); + } + } + } + + display(appearance, isInformationVisible, pathSmoothness){ + super.display(); + + if (isInformationVisible){ + + if (appearance === 'Circles'){ + let r = 0.5; + + fill(200); + this.openList.forEach(n => { + ellipse(n.pos.x, n.pos.y, r * 2); + }); + + fill(100); + this.closedList.forEach(n => { + if (n === this.startNode) + return; + ellipse(n.pos.x, n.pos.y, r * 2); + }); + } + + if (appearance === 'Rectangles'){ + fill(200); + this.openList.forEach(n => { + rect(n.pos.x - 0.5, n.pos.y - 0.5, 1, 1); + }); + + fill(100); + this.closedList.forEach(n => { + if (n === this.startNode) + return; + rect(n.pos.x - 0.5, n.pos.y - 0.5, 1, 1); + }); + } + } + + + noFill(); + stroke(50, 255, 50); + strokeWeight(0.2); + + let node; + if (!this.openList.length){ + node = this.closedList[this.closedList.length - 1]; + } else { + node = this.openList[this.minFNodeIndex]; + } + if (this.hasSucceeded){ + node = this.targetNode; + } + if (node){ + + if (pathSmoothness){ + beginShape(); + vertex(node.pos.x, node.pos.y); + while (node.predecessor){ + node = node.predecessor; + + if (!node.predecessor){ + quadraticVertex(node.pos.x, node.pos.y, node.pos.x, node.pos.y); + endShape(); + } else { + let cx = (node.pos.x + node.predecessor.pos.x) / 2; + let cy = (node.pos.y + node.predecessor.pos.y) / 2; + quadraticVertex(node.pos.x, node.pos.y, cx, cy); + } + } + + } else { + while (node.predecessor){ + line(node.pos.x, node.pos.y, node.predecessor.pos.x, node.predecessor.pos.y); + node = node.predecessor; + } + } + } + } + +} + +class Dijkstra extends Algorithm{ + + constructor(){ + super(); + + this.nodes = this.grid.allowedNodes.slice(); + this.nodes.forEach(n => n.distance = Infinity); + this.startNode.distance = 0; + } + + get minDistanceNodeIndex(){ + let index = 0, distance = this.nodes[0].distance; + this.nodes.forEach((n, i) => { + if (n.distance < distance){ + index = i; + distance = n.distance; + } + }); + return index; + } + + update(){ + if (!this.running){ + return; + } + super.update(); + + let index = this.minDistanceNodeIndex; + let currentNode = this.nodes[index]; + + this.nodes.splice(index, 1); + + if (currentNode === this.targetNode){ + this.success(); + } + + currentNode.successors.forEach(s => { + if (this.nodes.find(n => n === s)){ + let alternativeDistance = currentNode.distance + currentNode.pos.dist(s.pos); + if (alternativeDistance < s.distance){ + s.distance = alternativeDistance; + s.predecessor = currentNode; + } + } + }) + } + + display(appearance, isInformationVisible, pathSmoothness){ + super.display(); + + if (isInformationVisible){ + if (appearance === 'Circles'){ + let r = 0.5; + fill(100); + this.nodes.forEach(n => { + if (n === this.startNode || n === this.targetNode) + return; + ellipse(n.pos.x, n.pos.y, r * 2); + }); + } + + if (appearance === 'Rectangles'){ + fill(100); + this.nodes.forEach(n => { + if (n === this.startNode || n === this.targetNode) + return; + rect(n.pos.x - 0.5, n.pos.y - 0.5, 1, 1); + }); + } + } + + + noFill(); + stroke(50, 255, 50); + strokeWeight(0.2); + + let node = this.nodes[this.minDistanceNodeIndex]; + if (this.hasSucceeded){ + node = this.targetNode; + } + if (node){ + + if (pathSmoothness){ + beginShape(); + vertex(node.pos.x, node.pos.y); + while (node.predecessor){ + node = node.predecessor; + + if (!node.predecessor){ + quadraticVertex(node.pos.x, node.pos.y, node.pos.x, node.pos.y); + endShape(); + } else { + let cx = (node.pos.x + node.predecessor.pos.x) / 2; + let cy = (node.pos.y + node.predecessor.pos.y) / 2; + quadraticVertex(node.pos.x, node.pos.y, cx, cy); + } + } + + } else { + while (node.predecessor){ + line(node.pos.x, node.pos.y, node.predecessor.pos.x, node.predecessor.pos.y); + node = node.predecessor; + } + } + } + } + +} \ No newline at end of file diff --git a/public/data/scripts/events.js b/public/data/scripts/events.js new file mode 100644 index 0000000..3fb88f0 --- /dev/null +++ b/public/data/scripts/events.js @@ -0,0 +1,29 @@ +'use strict'; + +function keyPressed(){ + +} + +function keyReleased(){ + +} + +function mouseMoved(){ + +} + +function mouseDragged(){ + +} + +function mousePressed(){ + +} + +function mouseReleased(){ + +} + +function windowResized(){ + resizeCanvas($('#canvas-holder').width(), $('#canvas-holder').height()); +} \ No newline at end of file diff --git a/public/data/scripts/game.js b/public/data/scripts/game.js new file mode 100644 index 0000000..835648c --- /dev/null +++ b/public/data/scripts/game.js @@ -0,0 +1,111 @@ +class Game{ + + constructor(){ + this.generateGrid(); + this.updateVelocity(); + this.updateDifficulty(); + this.updateVisibilities(); + } + + generateGrid(){ + this.algorithm = null; + + let count = parseInt($('#cell_count').val()); + let movement = $('#movement').val(); + let difficulty = parseInt($('#difficulty').val()); + let hasRandomDirection = $('#direction').is(':checked'); + + this.grid = new Grid(localSettings.app.grid, count, difficulty, hasRandomDirection); + $('#cell_count_text').text(count + 'x' + count + ' nodes'); + + //Two main nodes, start and target + if (hasRandomDirection){ + this.startNode = random(this.grid.allowedNodes); + this.targetNode = random(this.grid.getAllowedNodesDistantTo(this.startNode) + .filter(n => { + if (movement === '1'){ + return n.pos.x % 2 === this.startNode.pos.x % 2 + && n.pos.y % 2 === this.startNode.pos.y % 2; + } + return true; + }) + ); + } else { + this.startNode = this.grid.nodes[0][0]; + this.targetNode = this.grid.nodes[count - 1][count - 1]; + } + + this.updateTester = 0; + } + + update(){ + if (this.algorithm){ + this.updateTester += this.movesPerSecond / frameRate(); + let i = 0; + for (; i < floor(this.updateTester); i++){ + this.algorithm.update(); + } + this.updateTester -= i; + } + } + + display(){ + this.grid.display(this.appearance, this.linesAreVisible); + + if (this.appearance === 'Circles'){ + let r = 0.5; + fill(50, 50, 255); + ellipse(this.startNode.pos.x, this.startNode.pos.y, r * 2); + fill(50, 255, 50); + ellipse(this.targetNode.pos.x, this.targetNode.pos.y, r * 2); + } + if (this.appearance === 'Rectangles'){ + fill(50, 50, 255); + rect(this.startNode.pos.x - 0.5, this.startNode.pos.y - 0.5, 1, 1); + fill(50, 255, 50); + rect(this.targetNode.pos.x - 0.5, this.targetNode.pos.y - 0.5, 1, 1); + } + + + if (this.algorithm) + this.algorithm.display(this.appearance, this.isInformationVisible, this.pathSmoothness); + } + + start(algorithm){ + this.algorithm = algorithm; + + let movement = $('#movement').val(); + this.grid.nodes.forEach(x => x.forEach(n => { + n.setMovement(movement); + n.connectToSuccessors(this.grid.nodes); + })); + } + + updateVelocity(){ + let value = parseInt($('#velocity').val()); + $('#velocity_text').text(value + ' moves per second'); + this.movesPerSecond = value; + } + + updateDifficulty(){ + let value = parseInt($('#difficulty').val()); + $('#difficulty_text').text('Difficulty: ' + value); + this.generateGrid(); + } + + updateVisibilities(){ + let appearance = $('input[name=appearance]:checked').val(); + let displayGrid = $('#gridVisibility').is(':checked'); + let displayInformation = $('#information').is(':checked'); + let pathSmoothness = $('#pathSmoothness').is(':checked'); + + if (!appearance) + appearance = $('input[name=appearance]:eq(0)').val(); + + this.appearance = appearance; + this.linesAreVisible = displayGrid; + this.isInformationVisible = displayInformation; + this.pathSmoothness = pathSmoothness; + } + +} diff --git a/public/data/scripts/grid.js b/public/data/scripts/grid.js new file mode 100644 index 0000000..fc81d38 --- /dev/null +++ b/public/data/scripts/grid.js @@ -0,0 +1,160 @@ +class Grid{ + + constructor(settings, cellCount, difficulty, randomDirection){ + + this.margin = settings.margin; + + this.cellCount = createVector(cellCount, cellCount); + + this.nodes = []; + for (let x = 0; x < this.cellCount.x; x++){ + let column = []; + for (let y = 0; y < this.cellCount.y; y++){ + let forbidden = ranBool(7 - difficulty); + if (!randomDirection){ + let allowed = [ + {x: 0, y: 0}, + {x: 1, y: 0}, + {x: 1, y: 1}, + {x: 0, y: 1}, + {x: this.cellCount.x - 1, y: this.cellCount.y - 1}, + {x: this.cellCount.x - 1, y: this.cellCount.y - 2}, + {x: this.cellCount.x - 2, y: this.cellCount.y - 2}, + {x: this.cellCount.x - 2, y: this.cellCount.y - 1}, + ]; + allowed.forEach(a => { + if (x === a.x && y === a.y){ + forbidden = false; + } + }); + } + column.push(new PathNode(x, y, forbidden)); + } + this.nodes.push(column); + } + + this.nodes.forEach(x => x.forEach(n => n.connectToSuccessors(this.nodes))); + + let nodes = []; + this.nodes.forEach(x => nodes.push(...x.filter(n => n.isForbidden))); + this.forbiddenNodes = nodes; + + nodes = []; + this.nodes.forEach(x => nodes.push(...x.filter(n => !n.isForbidden))); + this.allowedNodes = nodes; + } + + get size(){ + return Math.min(width - this.margin * 2, height - this.margin * 2); + } + + get nodeSize(){ + return { + x: this.size / this.cellCount.x, + y: this.size / this.cellCount.y + }; + } + + display(appearance, linesAreVisible){ + translate(this.margin, this.margin); + + if (linesAreVisible){ + let size = this.size; + stroke(100); + strokeWeight(1); + for (let x = 0; x <= size + 1; x += size / this.cellCount.x){ + line(x, 0, x, size); + } + for (let y = 0; y <= size + 1; y += size / this.cellCount.y){ + line(0, y, size, y); + } + } + + translate(this.nodeSize.x / 2, this.nodeSize.y / 2); + scale(this.nodeSize.x, this.nodeSize.y); + + noStroke(); + fill(200, 50, 50); + for (let n of this.forbiddenNodes){ + let x = n.pos.x; + let y = n.pos.y; + + if (appearance === 'Circles'){ + let r = 0.5; + ellipse(x, y, r * 2); + } + if (appearance === 'Rectangles'){ + rect(x - 0.5, y - 0.5, 1, 1); + } + } + } + + getAllowedNodesDistantTo(otherNode){ + return this.allowedNodes.filter(n => n.pos.dist(otherNode.pos) > Math.max(this.cellCount.x, this.cellCount.y) / 2); + } + +} + +class PathNode{ + + constructor(x, y, isForbidden){ + this.pos = createVector(x, y); + this.isForbidden = isForbidden; + } + + get distanceToTarget(){ + return this.pos.dist(game.targetNode.pos); + } + + setMovement(movement){ + this.isDiagonalAllowed = false; + this.isStraightAllowed = false; + switch (movement){ + case "0": + this.isStraightAllowed = true; + break; + case "1": + this.isDiagonalAllowed = true; + break; + case "2": + this.isStraightAllowed = true; + this.isDiagonalAllowed = true; + break; + } + } + + connectToSuccessors(nodes){ + let allowed = [], successors = []; + if (this.isStraightAllowed){ + allowed.push( + {x: 1, y: 0}, + {x: 0, y: 1}, + {x: -1, y: 0}, + {x: 0, y: -1} + ); + } + if (this.isDiagonalAllowed){ + allowed.push( + {x: 1, y: 1}, + {x: 1, y: -1}, + {x: -1, y: 1}, + {x: -1, y: -1} + ); + } + + allowed.forEach(a => { + let column = nodes[this.pos.x + a.x]; + if (column){ + let node = column[this.pos.y + a.y]; + if (node){ + if (!node.isForbidden){ + successors.push(node); + } + } + } + }); + + this.successors = successors; + } + +} \ No newline at end of file diff --git a/public/data/scripts/init.js b/public/data/scripts/init.js new file mode 100644 index 0000000..e0654fb --- /dev/null +++ b/public/data/scripts/init.js @@ -0,0 +1,87 @@ +'use strict'; + +let debug = false, + font, + localSettings, + loader; + +//Only for online games +let socket; + +let antiCacheQuery = '?_=' + new Date().getTime(); + +let game; + +function preload(){ + localSettings = loadJSON('data/settings/settings.json' + antiCacheQuery, json => { + console.log('Local settings loaded: ', json); + }, error => { + console.log('Local settings failed: ', error); + }); + + font = loadFont('data/styles/font.ttf', json => { + console.log('Local font loaded: ', json); + }, error => { + console.log('Local font failed: ', error); + }); +} + +function setup(){ + canvasSetup(); + interfaceSetup(); + + loadDynamicScripts().then(() => game = new Game()); +} + +function draw(){ + + background(40); + + if (game){ + game.update(); + game.display(); + } + + if (loader){ + loader.update(); + loader.display(); + } + + if (debug) debugInformation(); +} + +function canvasSetup(){ + setFrameRate(60); + let w = $('#canvas-holder').width(), + h = $('#canvas-holder').height(); + let canvas = createCanvas(w, h); + canvas.parent('canvas-holder'); + textFont(font); +} + +function interfaceSetup(){ + $('#cell_count').attr({ + 'min': localSettings.app.grid.size.min, + 'max': localSettings.app.grid.size.max + }); +} + +function loadDynamicScripts(){ + + return httpGet('data/settings/libraries.json' + antiCacheQuery, 'json') + .then(json => { + let requests = []; + for (let script in json){ + if (json[script]){ + let url = '/lib/benjocraeft/' + script + '.js' + requests.push($.getScript(url, () => { + console.log('Successfully loaded script: ', url) + })); + } + } + return $.when(...requests); + }) + .then(() => { + console.log("All dynamic scripts have been loaded!"); + }); +} \ No newline at end of file diff --git a/public/data/scripts/online.js b/public/data/scripts/online.js new file mode 100644 index 0000000..13d3176 --- /dev/null +++ b/public/data/scripts/online.js @@ -0,0 +1,10 @@ +'use strict'; + +function socketConnect(project, name = "noone"){ + let urlQueries = '?game=' + project.name + '&name=' + name; + let port = 3000; + let url = location.hostname + ":" + port + urlQueries; + + socket = io.connect(url); + socket.on('connect', () => console.log('Connected to ', url)); +} \ No newline at end of file diff --git a/public/data/settings/libraries.json b/public/data/settings/libraries.json new file mode 100644 index 0000000..e51971c --- /dev/null +++ b/public/data/settings/libraries.json @@ -0,0 +1,8 @@ +{ + "collision": false, + "colorPicker": false, + "cookie": false, + "loader": false, + "prototypes": false, + "technical": true +} \ No newline at end of file diff --git a/public/data/settings/settings.json b/public/data/settings/settings.json new file mode 100644 index 0000000..adf0732 --- /dev/null +++ b/public/data/settings/settings.json @@ -0,0 +1,32 @@ +{ + "project": { + "name": "pathfinder", + "author": "BenjoCraeft", + "version": "1.0.0", + "playerCounts": [], + "online": { + "iceServers": [ + {"urls": "stun:stun.l.google.com:19302"}, + { + "urls": "turn:numb.viagenie.ca", + "credential": "muazkh", + "username": "webrtc@live.com" + } + ] + } + }, + "frameWork": { + "frameRate": 60, + "width": null, + "height": null + }, + "app": { + "grid": { + "margin": 50, + "size": { + "min": 20, + "max": 100 + } + } + } +} \ No newline at end of file diff --git a/public/data/styles/color_picker.css b/public/data/styles/color_picker.css new file mode 100644 index 0000000..a5b510e --- /dev/null +++ b/public/data/styles/color_picker.css @@ -0,0 +1,88 @@ +#color_picker{ + width: 300px; + height: 25%; + margin: 20px; + margin-top: 50px; + border: 5px solid #000; + background-color: #000; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + position: relative; +} +#color_picker_numeric{ + width: 80%; + padding: 5%; + margin: 5%; + background-color: #888; + border-radius: 10px; + overflow: hidden; +} +.color_picker_rgb{ + float: left; + width: 22%; + height: 35px; + font-size: 25px; + color: #000; +} +.color_picker_rgb:nth-child(1){ + margin-right: 10%; + margin-left: 3%; + background-color: #F00; + +} +.color_picker_rgb:nth-child(2){ + background-color: #0F0; +} +.color_picker_rgb:nth-child(3){ + margin-left: 10%; + background-color: #00F; + color: #FFF; +} +#color_picker_hex{ + width: 50%; + height: 30px; + font-size: 25px; + margin: 10% 25% 0 25%; +} +#saturation{ + position: relative; + width: calc(100% - 33px); + height: 100%; + background: linear-gradient(to right, #FFF 0%, #F00 100%); + float: left; + margin-right: 6px; +} +#value { + width: 100%; + height: 100%; + background: linear-gradient(to top, #000 0%, rgba(255,255,255,0) 100%); +} +#sb_picker{ + border: 2px solid; + border-color: #FFF; + position: absolute; + width: 14px; + height: 14px; + border-radius: 10px; + bottom: 50px; + left: 50px; + box-sizing: border-box; + z-index: 10; +} +#hue { + width: 27px; + height: 100%; + position: relative; + float: left; + background: linear-gradient(to bottom, #F00 0%, #F0F 17%, #00F 34%, #0FF 50%, #0F0 67%, #FF0 84%, #F00 100%); +} +#hue_picker { + position: absolute; + background: #000; + border-bottom: 1px solid #000; + top: 0; + width: 27px; + height: 2px; +} \ No newline at end of file diff --git a/public/data/styles/font.ttf b/public/data/styles/font.ttf new file mode 100644 index 0000000..199cf40 Binary files /dev/null and b/public/data/styles/font.ttf differ diff --git a/public/data/styles/range_input.css b/public/data/styles/range_input.css new file mode 100644 index 0000000..daddfb1 --- /dev/null +++ b/public/data/styles/range_input.css @@ -0,0 +1,88 @@ +input[type=range] { + -webkit-appearance: none; + margin: 18px 0; + width: 100%; + background: none; +} +input[type=range]:focus { + outline: none; +} +input[type=range]::-webkit-slider-runnable-track { + width: 100%; + height: 8.4px; + cursor: pointer; + animate: 0.2s; + box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; + background: #3071a9; + border-radius: 1.3px; + border: 0.2px solid #010101; +} +input[type=range]::-webkit-slider-thumb { + box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; + border: 1px solid #000000; + height: 36px; + width: 16px; + border-radius: 3px; + background: #ffffff; + cursor: pointer; + -webkit-appearance: none; + margin-top: -14px; +} +input[type=range]:focus::-webkit-slider-runnable-track { + background: #367ebd; +} +input[type=range]::-moz-range-track { + width: 100%; + height: 8.4px; + cursor: pointer; + box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; + background: #3071a9; + border-radius: 1.3px; + border: 0.2px solid #010101; +} +input[type=range]::-moz-range-thumb { + box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; + border: 1px solid #000000; + height: 36px; + width: 16px; + border-radius: 3px; + background: #302a86; + cursor: pointer; +} +input[type=range]::-ms-track { + width: 100%; + height: 8.4px; + cursor: pointer; + animate: 0.2s; + background: transparent; + border-color: transparent; + border-width: 16px 0; + color: transparent; +} +input[type=range]::-ms-fill-lower { + background: #2a6495; + border: 0.2px solid #010101; + border-radius: 2.6px; + box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; +} +input[type=range]::-ms-fill-upper { + background: #3071a9; + border: 0.2px solid #010101; + border-radius: 2.6px; + box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; +} +input[type=range]::-ms-thumb { + box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; + border: 1px solid #000000; + height: 36px; + width: 16px; + border-radius: 3px; + background: #ffffff; + cursor: pointer; +} +input[type=range]:focus::-ms-fill-lower { + background: #3071a9; +} +input[type=range]:focus::-ms-fill-upper { + background: #367ebd; +} \ No newline at end of file diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..8b250b4 --- /dev/null +++ b/public/index.html @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + Pathfinding Algorithms + + +
+
+
+
+
+ + +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + + + + + + + + + +
+
+ + \ No newline at end of file diff --git a/public/styles.css b/public/styles.css new file mode 100644 index 0000000..1f1b48a --- /dev/null +++ b/public/styles.css @@ -0,0 +1,85 @@ +a:link, a:hover, a:active, a:visited{color: #000;} + +html, body{margin: 0; padding: 0; height: 100%; width: 100%;} + +canvas{margin: 0; padding: 0; border: none; display: block;} + +button:hover{cursor: pointer;} + +@font-face{ + font-family: "Rametto"; + src: url("data/styles/font.ttf"); +} + +*{ + font-family: "Rametto", serif; + color: #000; + font-size: 17px; +} + +:root{ + --width: 100vw; + --height: 100vh; +} + +/** + * Standard styles + */ + +#canvas-holder{ + position: relative; + width: 100vh; + height: 100vh; + float: left; +} +#canvas-holder canvas{ + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: inherit; +} +#p5_loading{ + display: none; +} + + + +/** + * Interface + */ + +#interface{ + width: calc(100% - 100vh); + height: 80vh; + padding: 10vh 0; + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + + float: left; + background-color: rgb(40, 40, 40); +} + +#interface > *{ + margin: 10px; + width: 300px; + text-align: center; +} + +span, label, select{ + color: rgb(0, 0, 0); +} + +button{ + background-color: rgb(50, 200, 50); + border: 2px solid #000; +} +button:hover{ + background-color: rgb(20, 100, 20); +} + + + diff --git a/public/thumbnail.png b/public/thumbnail.png new file mode 100644 index 0000000..604e8df Binary files /dev/null and b/public/thumbnail.png differ