init'
This commit is contained in:
53
tutorials/TSP_files/index.php
Normal file
53
tutorials/TSP_files/index.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
if($_SERVER['HTTP_X_FORWARDED_PROTO'] !== 'https') {
|
||||
header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
|
||||
die();
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<link rel="shortcut icon" type"image/ico" href"../Media/iconImage.ico">
|
||||
<link href="style.css" rel="stylesheet" type="text/css">
|
||||
|
||||
<script
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.2.0/p5.min.js"
|
||||
integrity="sha512-b/htz6gIyFi3dwSoZ0Uv3cuv3Ony7EeKkacgrcVg8CMzu90n777qveu0PBcbZUA7TzyENGtU+qZRuFAkfqgyoQ=="
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
|
||||
<title>Travelling Sales Person</title>
|
||||
<h1>Travelling Sales Person Problem</h1>
|
||||
<button id="backButton" onclick="window.location.href='../../tutorials.html'">Back</button>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<div class="pictureContainer">
|
||||
<img src="https://optimization.mccormick.northwestern.edu/images/e/ea/48StatesTSP.png"
|
||||
alt="TSP Problem" id="Conway">
|
||||
|
||||
</div>
|
||||
|
||||
<p>The travelling salesman problem (TSP) asks the following question: "Given a list of cities and the distances between each pair of cities, what is the shortest possible route that visits each city and returns to the origin city?" </p>
|
||||
<p>The problem was first formulated in 1930 and is one of the most intensively studied problems in optimization. It is used as a benchmark for many optimization methods. Even though the problem is computationally difficult, a large number of heuristics and exact algorithms are known, so that some instances with tens of thousands of cities can be solved completely and even problems with millions of cities can be approximated within a small fraction of 1%.</p>
|
||||
<p>The TSP has several applications even in its purest formulation, such as planning, logistics, and the manufacture of microchips. Slightly modified, it appears as a sub-problem in many areas, such as DNA sequencing. In these applications, the concept city represents, for example, customers, soldering points, or DNA fragments, and the concept distance represents travelling times or cost, or a similarity measure between DNA fragments. The TSP also appears in astronomy, as astronomers observing many sources will want to minimize the time spent moving the telescope between the sources. In many applications, additional constraints such as limited resources or time windows may be imposed.</p>
|
||||
<h2>These are some of the algorithms I used</h2>
|
||||
<p>Note the purple route is the best route it's found so far and the thin white lines are the routes it's trying real time.</p>
|
||||
</head>
|
||||
<body>
|
||||
<div class="canvasBody">
|
||||
<h3>Random Sort</h3>
|
||||
<span id="c1"></span>
|
||||
<p class="canvasText">This canvas sorts through random possiblities. Every frame the program chooses two random points (cities) and swaps them around. eg say the order was London, Paris, Madrid, the program would swap London and Paris so that the new order is: Paris, London, Madrid. The program then compares the distance against the record distance to decide whether the new order is better than the old order. This search method is the most inefficient way, the worst case scenario is never ending, as the point swaping is random the program may never reach the optimum route</p><br>
|
||||
<h3>Lexicographic Order</h3>
|
||||
<span id="c2"></span>
|
||||
<p class="canvasText">This canvas sorts through all possible orders sequentially, so after n! (where n is the number of points) this algorithm is guaranteed to have found the quickest possible route. However it is highly inefficient always taking n! frames to complete and as n increases, time taken increases exponentially.</p>
|
||||
<a target="_blank" href="https://www.quora.com/How-would-you-explain-an-algorithm-that-generates-permutations-using-lexicographic-ordering">Click here to learn more about the algorithm</a><br>
|
||||
<h3>Genetic Algorithm</h3>
|
||||
<span id="c3"></span>
|
||||
<p class="canvasText">This canvas is the most efficient at finding the quickest route, it is a mixture of the two methods above. It starts off by creating a population of orders, a fitness is then generated for each order in the population. This fitness decides how likely the order is to be picked and is based on the distance it takes (lower distance is better). When two orders are picked, the algorithm splices the two together at a random term, it's then mutated and compared against the record distance. This takes the least amount of time to find the shortest distance as the algorithm doesn't search through permuations that are obviously longer due to the order.</p><br>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script src="sketch.js"></script>
|
||||
|
||||
<footer><p>This page was inspired by The Coding Train</p><a href="https://www.youtube.com/channel/UCvjgXvBlbQiydffZU7m1_aw">Check him out here</a></footer>
|
||||
</html>
|
||||
382
tutorials/TSP_files/sketch.js
Normal file
382
tutorials/TSP_files/sketch.js
Normal file
@@ -0,0 +1,382 @@
|
||||
/*var seedSlider = document.getElementById('sliderRange')
|
||||
seedSlider.onChange = function(){
|
||||
return this.value;
|
||||
}
|
||||
|
||||
var citiesSlider = document.getElementById('nRange')
|
||||
citiesSlider.onChange = function(){
|
||||
return this.value;
|
||||
}*/
|
||||
|
||||
var Seed = 21//seedChange(11);
|
||||
var totalCities = 25//citiesChange(9);
|
||||
// Sketch One
|
||||
var randomSearch = function( p ) { // p could be any variable name
|
||||
var cities = [];
|
||||
var recordDistance;
|
||||
var bestEver;
|
||||
var localSeed = Seed;
|
||||
var localCities = totalCities;
|
||||
p.setup = function() {
|
||||
p.createCanvas(400, 400);
|
||||
p.setupPoints(Seed, totalCities);
|
||||
}
|
||||
|
||||
p.setupPoints = function(rngSeed, numCities){
|
||||
p.randomSeed(rngSeed);
|
||||
for (var i = 0; i < numCities; i++) {
|
||||
var v = p.createVector(p.random(p.width), p.random(p.height/2));
|
||||
cities[i] = v;
|
||||
}
|
||||
|
||||
var d = p.calcDistance(cities);
|
||||
recordDistance = d;
|
||||
bestEver = cities.slice();
|
||||
}
|
||||
|
||||
|
||||
p.draw = function() {
|
||||
if(localSeed != Seed){
|
||||
p.setupPoints(Seed, localCities);
|
||||
localSeed = Seed;
|
||||
}
|
||||
if(localCities != totalCities){
|
||||
setupPoints(localSeed, totalCities);
|
||||
localCities = totalCities;
|
||||
}
|
||||
|
||||
p.background(0);
|
||||
|
||||
p.fill(255);
|
||||
p.stroke(255);
|
||||
for (var i = 0; i < cities.length; i++) {
|
||||
p.ellipse(cities[i].x, cities[i].y, 8, 8);
|
||||
}
|
||||
|
||||
p.stroke(255, 0, 255);
|
||||
p.strokeWeight(4);
|
||||
p.noFill();
|
||||
p.beginShape();
|
||||
for (var i = 0; i < cities.length; i++) {
|
||||
p.vertex(bestEver[i].x, bestEver[i].y);
|
||||
}
|
||||
p.endShape();
|
||||
|
||||
p.stroke(255);
|
||||
p.translate(0 , p.height/2);
|
||||
p.strokeWeight(1);
|
||||
p.noFill();
|
||||
p.beginShape();
|
||||
for (var i = 0; i < cities.length; i++) {
|
||||
p.vertex(cities[i].x, cities[i].y);
|
||||
}
|
||||
p.endShape();
|
||||
|
||||
var i = p.floor(p.random(cities.length));
|
||||
var j = p.floor(p.random(cities.length));
|
||||
p.swap(cities, i, j);
|
||||
|
||||
var d = p.calcDistance(cities);
|
||||
if (d < recordDistance) {
|
||||
recordDistance = d;
|
||||
bestEver = cities.slice();
|
||||
}
|
||||
}
|
||||
|
||||
p.swap = function(a, i, j) {
|
||||
var temp = a[i];
|
||||
a[i] = a[j];
|
||||
a[j] = temp;
|
||||
}
|
||||
|
||||
|
||||
p.calcDistance = function(points) {
|
||||
var sum = 0;
|
||||
for (var i = 0; i < points.length - 1; i++) {
|
||||
var d = p.dist(points[i].x, points[i].y, points[i + 1].x, points[i + 1].y);
|
||||
sum += d;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
};
|
||||
|
||||
var myp5 = new p5(randomSearch, 'c1');
|
||||
|
||||
// Sketch Two
|
||||
var lexicographicOrder = function( q ) {
|
||||
var cities = [];
|
||||
var order = [];
|
||||
var totalPermutations;
|
||||
var count = 0;
|
||||
var recordDistance;
|
||||
var bestEver;
|
||||
|
||||
q.setup = function() {
|
||||
q.randomSeed(Seed);
|
||||
q.createCanvas(400, 400);
|
||||
for (var i = 0; i < totalCities; i++) {
|
||||
var v = q.createVector(q.random(q.width), q.random(q.height / 2));
|
||||
cities[i] = v;
|
||||
order[i] = i;
|
||||
}
|
||||
|
||||
var d = q.calcDistance(cities, order);
|
||||
recordDistance = d;
|
||||
bestEver = order.slice();
|
||||
}
|
||||
|
||||
q.draw = function() {
|
||||
q.background(0);
|
||||
|
||||
q.fill(255);
|
||||
for (var i = 0; i < cities.length; i++) {
|
||||
q.ellipse(cities[i].x, cities[i].y, 8, 8);
|
||||
}
|
||||
|
||||
q.stroke(255, 0, 255);
|
||||
q.strokeWeight(4);
|
||||
q.noFill();
|
||||
q.beginShape();
|
||||
for (var i = 0; i < order.length; i++) {
|
||||
var n = bestEver[i];
|
||||
q.vertex(cities[n].x, cities[n].y);
|
||||
}
|
||||
q.endShape();
|
||||
|
||||
|
||||
q.translate(0, q.height / 2);
|
||||
q.stroke(255);
|
||||
q.strokeWeight(1);
|
||||
q.noFill();
|
||||
q.beginShape();
|
||||
for (var i = 0; i < order.length; i++) {
|
||||
var n = order[i];
|
||||
q.vertex(cities[n].x, cities[n].y);
|
||||
}
|
||||
q.endShape();
|
||||
|
||||
var d = q.calcDistance(cities, order);
|
||||
if (d < recordDistance) {
|
||||
recordDistance = d;
|
||||
bestEver = order.slice();
|
||||
}
|
||||
|
||||
q.nextOrder();
|
||||
}
|
||||
|
||||
q.swap = function(a, i, j) {
|
||||
var temp = a[i];
|
||||
a[i] = a[j];
|
||||
a[j] = temp;
|
||||
}
|
||||
|
||||
|
||||
q.calcDistance = function(points, order) {
|
||||
var sum = 0;
|
||||
for (var i = 0; i < order.length - 1; i++) {
|
||||
var cityAIndex = order[i];
|
||||
var cityA = points[cityAIndex];
|
||||
var cityBIndex = order[i + 1];
|
||||
var cityB = points[cityBIndex];
|
||||
var d = q.dist(cityA.x, cityA.y, cityB.x, cityB.y);
|
||||
sum += d;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
// This is my lexical order algorithm
|
||||
|
||||
q.nextOrder = function() {
|
||||
|
||||
// STEP 1 of the algorithm
|
||||
// https://www.quora.com/How-would-you-explain-an-algorithm-that-generates-permutations-using-lexicographic-ordering
|
||||
var largestI = -1;
|
||||
for (var i = 0; i < order.length - 1; i++) {
|
||||
if (order[i] < order[i + 1]) {
|
||||
largestI = i;
|
||||
}
|
||||
}
|
||||
if (largestI == -1) {
|
||||
q.noLoop();
|
||||
}
|
||||
|
||||
// STEP 2
|
||||
var largestJ = -1;
|
||||
for (var j = 0; j < order.length; j++) {
|
||||
if (order[largestI] < order[j]) {
|
||||
largestJ = j;
|
||||
}
|
||||
}
|
||||
|
||||
// STEP 3
|
||||
q.swap(order, largestI, largestJ);
|
||||
|
||||
// STEP 4: reverse from largestI + 1 to the end
|
||||
var endArray = order.splice(largestI + 1);
|
||||
endArray.reverse();
|
||||
order = order.concat(endArray);
|
||||
}
|
||||
|
||||
};
|
||||
var myp5 = new p5(lexicographicOrder, 'c2');
|
||||
|
||||
var geneticSearch = function( p ) {
|
||||
var cities = [];
|
||||
var popSize = 500;
|
||||
var population = [];
|
||||
var fitness = [];
|
||||
var recordDistance = Infinity;
|
||||
var bestEver;
|
||||
var currentBest;
|
||||
var statusP;
|
||||
|
||||
p.setup = function(){
|
||||
p.randomSeed(Seed);
|
||||
p.createCanvas(400, 400);
|
||||
var order = [];
|
||||
for (var i = 0; i < totalCities; i++) {
|
||||
var v = p.createVector(p.random(p.width), p.random(p.height / 2));
|
||||
cities[i] = v;
|
||||
order[i] = i;
|
||||
}
|
||||
|
||||
for (var i = 0; i < popSize; i++) {
|
||||
population[i] = p.shuffle(order);
|
||||
}
|
||||
statusP = p.createP('').style('font-size', '32pt');
|
||||
}
|
||||
|
||||
p.draw = function() {
|
||||
p.background(0);
|
||||
p.stroke(255);
|
||||
p.fill(255);
|
||||
for (var i = 0; i < cities.length; i++) {
|
||||
p.ellipse(cities[i].x, cities[i].y, 8, 8);
|
||||
}
|
||||
|
||||
p.calculateFitness();
|
||||
p.normalizeFitness();
|
||||
p.nextGeneration();
|
||||
|
||||
p.noFill();
|
||||
p.strokeWeight(4);
|
||||
p.beginShape();
|
||||
for (var i = 0; i < bestEver.length; i++) {
|
||||
var n = bestEver[i];
|
||||
p.stroke(255, 0, 255);
|
||||
p.vertex(cities[n].x, cities[n].y);
|
||||
//p.stroke(255);
|
||||
//p.ellipse(cities[n].x, cities[n].y, 8, 8);
|
||||
}
|
||||
p.endShape();
|
||||
|
||||
p.translate(0, p.height / 2);
|
||||
p.stroke(255);
|
||||
p.strokeWeight(1);
|
||||
p.noFill();
|
||||
p.beginShape();
|
||||
for (var i = 0; i < currentBest.length; i++) {
|
||||
var n = currentBest[i];
|
||||
p.vertex(cities[n].x, cities[n].y);
|
||||
//p.ellipse(cities[n].x, cities[n].y, 8, 8);
|
||||
}
|
||||
p.endShape();
|
||||
}
|
||||
|
||||
p.swap = function(a, i, j) {
|
||||
var temp = a[i];
|
||||
a[i] = a[j];
|
||||
a[j] = temp;
|
||||
}
|
||||
|
||||
p.calcDistance = function(points, order) {
|
||||
var sum = 0;
|
||||
for (var i = 0; i < order.length - 1; i++) {
|
||||
var cityAIndex = order[i];
|
||||
var cityA = points[cityAIndex];
|
||||
var cityBIndex = order[i + 1];
|
||||
var cityB = points[cityBIndex];
|
||||
var d = p.dist(cityA.x, cityA.y, cityB.x, cityB.y);
|
||||
sum += d;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
p.calculateFitness = function() {
|
||||
var currentRecord = Infinity;
|
||||
for (var i = 0; i < population.length; i++) {
|
||||
var d = p.calcDistance(cities, population[i]);
|
||||
if (d < recordDistance) {
|
||||
recordDistance = d;
|
||||
bestEver = population[i];
|
||||
}
|
||||
if (d < currentRecord) {
|
||||
currentRecord = d;
|
||||
currentBest = population[i];
|
||||
}
|
||||
|
||||
fitness[i] = 1 / (p.pow(d, 8) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
p.normalizeFitness = function() {
|
||||
var sum = 0;
|
||||
for (var i = 0; i < fitness.length; i++) {
|
||||
sum += fitness[i];
|
||||
}
|
||||
for (var i = 0; i < fitness.length; i++) {
|
||||
fitness[i] = fitness[i] / sum;;
|
||||
}
|
||||
}
|
||||
|
||||
p.nextGeneration = function() {
|
||||
var newPopulation = [];
|
||||
for (var i = 0; i < population.length; i++) {
|
||||
var orderA = p.pickOne(population, fitness);
|
||||
var orderB = p.pickOne(population, fitness);
|
||||
var order = p.crossOver(orderA, orderB);
|
||||
p.mutate(order, 0.01);
|
||||
newPopulation[i] = order;
|
||||
}
|
||||
population = newPopulation;
|
||||
|
||||
}
|
||||
|
||||
p.pickOne = function(list, prob) {
|
||||
var index = 0;
|
||||
var r = p.random(1);
|
||||
|
||||
while (r > 0) {
|
||||
r = r - prob[index];
|
||||
index++;
|
||||
}
|
||||
index--;
|
||||
return list[index].slice();
|
||||
}
|
||||
|
||||
p.crossOver = function(orderA, orderB) {
|
||||
var start = p.floor(p.random(orderA.length));
|
||||
var end = p.floor(p.random(start + 1, orderA.length));
|
||||
var neworder = orderA.slice(start, end);
|
||||
// var left = totalCities - neworder.length;
|
||||
for (var i = 0; i < orderB.length; i++) {
|
||||
var city = orderB[i];
|
||||
if (!neworder.includes(city)) {
|
||||
neworder.push(city);
|
||||
}
|
||||
}
|
||||
return neworder;
|
||||
}
|
||||
|
||||
|
||||
p.mutate = function(order, mutationRate) {
|
||||
for (var i = 0; i < totalCities; i++) {
|
||||
if (p.random(1) < mutationRate) {
|
||||
var indexA = p.floor(p.random(order.length));
|
||||
var indexB = (indexA + 1) % totalCities;
|
||||
p.swap(order, indexA, indexB);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var myp5 = new p5(geneticSearch, 'c3');
|
||||
160
tutorials/TSP_files/style.css
Normal file
160
tutorials/TSP_files/style.css
Normal file
@@ -0,0 +1,160 @@
|
||||
@import url('https://fonts.googleapis.com/css?family=Roboto+Condensed');
|
||||
|
||||
a{font-family: 'Roboto Condensed', sans-serif; font-size: 18pt;}
|
||||
|
||||
h1 {
|
||||
font-family: 'Roboto Condensed', sans-serif;
|
||||
margin: 0;
|
||||
padding: 0 0 15px 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-family: 'Roboto Condensed', sans-serif;
|
||||
margin: 0;
|
||||
padding: 0 0 15px 0;
|
||||
text-align: center;
|
||||
text-decoration: bold;
|
||||
}
|
||||
|
||||
@media (min-width: 350px) {
|
||||
h1 {font-size: 3.25em;}
|
||||
img{height: 40px;}
|
||||
p{font-size: 10px;}
|
||||
h2{font-size: 17px;}
|
||||
}
|
||||
|
||||
@media (min-width: 400px) {
|
||||
h1 {font-size: 3.25em;}
|
||||
img{height: 45px;}
|
||||
p{font-size: 15px;}
|
||||
h2{font-size: 17px;}
|
||||
}
|
||||
@media (min-width: 440px) {
|
||||
h1 {font-size: 3.5em;}
|
||||
img {height: 100px;}
|
||||
p{font-size: 16px;}
|
||||
h2{font-size: 18px;}
|
||||
}
|
||||
@media (min-width: 500px) {
|
||||
h1 {font-size: 3.75em;}
|
||||
img{height: 125px;}
|
||||
p{font-size: 16px;}
|
||||
h2{font-size: 19px;}
|
||||
}
|
||||
@media (min-width: 630px) {
|
||||
h1 {font-size: 5em;}
|
||||
img{height: 150px;}
|
||||
p{font-size: 20px;}
|
||||
h2{font-size: 24px;}
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
h1 {
|
||||
font-size: 5em;
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
img{height: 175px;}
|
||||
p{font-size: 22px;}
|
||||
h2{font-size: 26px;}
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h1 {font-size: 8em;}
|
||||
img{height: 250px;}
|
||||
p{font-size: 24px;}
|
||||
h2{font-size: 28px;}
|
||||
}
|
||||
|
||||
p{
|
||||
|
||||
font-family: 'Roboto Condensed', sans-serif;
|
||||
}
|
||||
|
||||
h3{
|
||||
text-align: center;
|
||||
font-size: 30px;
|
||||
font-family: 'Roboto Condensed', sans-serif;
|
||||
}
|
||||
|
||||
footer{
|
||||
padding: 20px;
|
||||
background-color: #e0e0e0;
|
||||
font-family: 'Roboto Condensed', sans-serif;
|
||||
}
|
||||
|
||||
@keyframes dimImg{
|
||||
from {opacity: 1;
|
||||
filter: alpha(opacity=100);}
|
||||
to {opacity: 0.4;
|
||||
filter: alpha(opacity=50);}
|
||||
}
|
||||
|
||||
@keyframes revealText{
|
||||
from {opacity: 0.4;
|
||||
filter: alpha(opacity=50);}
|
||||
to {opacity: 1;
|
||||
filter: alpha(opacity=100);}
|
||||
}
|
||||
|
||||
.pictureContainer{
|
||||
float: right;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.pictureContainer a{
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
}
|
||||
|
||||
|
||||
.pictureContainer:hover img{
|
||||
|
||||
animation-name: dimImg;
|
||||
animation-duration: 1s;
|
||||
opacity: 0.4;
|
||||
filter: alpha(opacity=50);
|
||||
}
|
||||
|
||||
.pictureContainer:hover a{
|
||||
animation-name: revealText;
|
||||
animation-duration: 1s;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.canvasText{
|
||||
margin: 0px;
|
||||
display: inline-block;
|
||||
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#c1, #c2, #c3{
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.button{
|
||||
padding: 16px 32px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
margin: 4px 2px;
|
||||
-webkit-transition-duration: 0.4s; /* Safari */
|
||||
transition-duration: 0.4s;
|
||||
cursor: pointer;
|
||||
background-color: white;
|
||||
color: black;
|
||||
border: 2px solid #555555;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background-color: #555555;
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
8
tutorials/ellipseConst/dot.js
Normal file
8
tutorials/ellipseConst/dot.js
Normal file
@@ -0,0 +1,8 @@
|
||||
class dot{
|
||||
|
||||
constructor(x, y){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
}
|
||||
60
tutorials/ellipseConst/index.php
Normal file
60
tutorials/ellipseConst/index.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
if($_SERVER['HTTP_X_FORWARDED_PROTO'] !== 'https') {
|
||||
header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
|
||||
die();
|
||||
}
|
||||
?>
|
||||
|
||||
<DOCTYPE! html>
|
||||
<html>
|
||||
<title>Constructing Ellipses</title>
|
||||
<p>Drag your cursor around the canvas (you can use the left and right buttons for different results) to contruct an ellipse</p>
|
||||
<p>Inspired by Grant Sanderson from 3blue1brown</p>
|
||||
<p>Watch his <a href="https://twitter.com/3blue1brown/status/1016936129117937664">here</a></p>
|
||||
<p>NOTE: doesn't work on mobile, go get a PC</p>
|
||||
<div class="quantity">
|
||||
<input id="numPoints" type="number" min="10" max="100" step="10" value="10">
|
||||
</div>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
|
||||
<script src="../frameworks/p5.min.js"></script>
|
||||
<script src="../frameworks/p5.dom.min.js"></script>
|
||||
<script src="../frameworks/jquery-3.3.1.min.js"></script>
|
||||
<script src="script.js"></script>
|
||||
<script src="dot.js"></script>
|
||||
<script>
|
||||
jQuery('<div class="quantity-nav"><div class="quantity-button quantity-up">+</div><div class="quantity-button quantity-down">-</div></div>').insertAfter('.quantity input');
|
||||
jQuery('.quantity').each(function() {
|
||||
var spinner = jQuery(this),
|
||||
input = spinner.find('input[type="number"]'),
|
||||
btnUp = spinner.find('.quantity-up'),
|
||||
btnDown = spinner.find('.quantity-down'),
|
||||
min = input.attr('min'),
|
||||
max = input.attr('max');
|
||||
|
||||
btnUp.click(function() {
|
||||
var oldValue = parseFloat(input.val());
|
||||
if (oldValue >= max) {
|
||||
var newVal = oldValue;
|
||||
} else {
|
||||
var newVal = oldValue + 1;
|
||||
}
|
||||
spinner.find("input").val(newVal);
|
||||
spinner.find("input").trigger("change");
|
||||
});
|
||||
|
||||
btnDown.click(function() {
|
||||
var oldValue = parseFloat(input.val());
|
||||
if (oldValue <= min) {
|
||||
var newVal = oldValue;
|
||||
} else {
|
||||
var newVal = oldValue - 1;
|
||||
}
|
||||
spinner.find("input").val(newVal);
|
||||
spinner.find("input").trigger("change");
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
138
tutorials/ellipseConst/script.js
Normal file
138
tutorials/ellipseConst/script.js
Normal file
@@ -0,0 +1,138 @@
|
||||
var ux, uy;
|
||||
var points = [];
|
||||
var numericValue;
|
||||
|
||||
function setup(){
|
||||
createCanvas(800, 800);
|
||||
background(41);
|
||||
}
|
||||
|
||||
function draw(){
|
||||
numericValue = document.getElementById('numPoints').value;
|
||||
translate(width/2, height/2);
|
||||
background(41);
|
||||
|
||||
stroke(75, 75, 215);
|
||||
strokeWeight(2);
|
||||
noFill();
|
||||
ellipse(0, 0, width-(0.0625*width), height - (0.0625*height));
|
||||
|
||||
stroke(215, 215, 15);
|
||||
strokeWeight(7);
|
||||
point(0,0);
|
||||
if (mouseY > 0 && mouseY < height && mouseX > 0 && mouseX < width){
|
||||
if (mouseIsPressed == true && mouseButton == LEFT){
|
||||
|
||||
ux = mouseX - width/2;
|
||||
uy = mouseY - height/2;
|
||||
point(ux, uy);
|
||||
getCirclePoints();
|
||||
drawLines(ux, uy);
|
||||
drawTLines(ux, uy);
|
||||
|
||||
} else if(mouseIsPressed == true && mouseButton == RIGHT){
|
||||
ux = mouseX - width/2;
|
||||
uy = mouseY - height/2;
|
||||
point(ux, uy);
|
||||
getCirclePoints();
|
||||
drawTLines(ux, uy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
function getCirclePoints(){
|
||||
var r = (width-(0.0625*width)) / 2;
|
||||
var step = 1 / numericValue;
|
||||
var index = 0;
|
||||
for (var i = 0; i < TWO_PI; i += step){
|
||||
var cx = r * Math.sin(i);
|
||||
var cy = r * Math.cos(i);
|
||||
points[index] = new dot(cx, cy);
|
||||
index ++;
|
||||
}
|
||||
}
|
||||
|
||||
function drawLines(startX, startY){
|
||||
strokeWeight(0.4);
|
||||
stroke(255);
|
||||
|
||||
for (var i = 0; i < points.length; i ++){
|
||||
line(startX, startY, points[i].x, points[i].y);
|
||||
//findMidpoint(startX, startY, points[i].x, points[i].y);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function drawTLines(startX, startY){
|
||||
strokeWeight(0.4);
|
||||
stroke(255);
|
||||
|
||||
for (var i = 0; i < points.length; i ++){
|
||||
findMidpoint(startX, startY, points[i].x, points[i].y);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function findMidpoint(x1, y1, x2, y2){
|
||||
//find center
|
||||
var cx = (x1+x2)/2;
|
||||
var cy = (y1+y2)/2;
|
||||
|
||||
//move line to the center on the origin
|
||||
x1 -= cx; y1 -= cy;
|
||||
x2 -= cx; y2 -= cy;
|
||||
|
||||
//rotate both points
|
||||
xtemp = x1; ytemp = y1;
|
||||
x1=-ytemp; y1=xtemp;
|
||||
|
||||
xtemp = x2; ytemp = y2;
|
||||
x2=-ytemp; y2=xtemp;
|
||||
|
||||
//move the center point back to where it was
|
||||
x1+=cx; y1+=cy;
|
||||
x2+=cx; y2+=cy;
|
||||
stroke(255, 0, 0);
|
||||
line(x1, y1, x2, y2);
|
||||
stroke(255);
|
||||
}
|
||||
|
||||
function genLines(){
|
||||
var pointOk = false;
|
||||
do{
|
||||
ux = random(width);
|
||||
uy = random(height);
|
||||
if(getDist(0, ux, 0, uy) <= (width-(0.0625*width)) / 2){
|
||||
pointOk = true;
|
||||
}
|
||||
}
|
||||
while(!pointOk);
|
||||
|
||||
point(ux, uy);
|
||||
getCirclePoints();
|
||||
drawLines(ux, uy);
|
||||
drawTLines(ux, uy);
|
||||
}
|
||||
|
||||
function genTLines(){
|
||||
var pointOk = false;
|
||||
do{
|
||||
ux = random(width);
|
||||
uy = random(height);
|
||||
if(getDist(0, ux, 0, uy) <= (width-(0.0625*width)) / 2){
|
||||
pointOk = true;
|
||||
}
|
||||
}
|
||||
while(!pointOk);
|
||||
|
||||
point(ux, uy);
|
||||
getCirclePoints();
|
||||
drawTLines(ux, uy);
|
||||
}
|
||||
|
||||
function getDist(x1, x2, y1, y2){
|
||||
return Math.sqrt((x1 - x2)^2 + (y1 - y2)^2);
|
||||
}
|
||||
76
tutorials/ellipseConst/style.css
Normal file
76
tutorials/ellipseConst/style.css
Normal file
@@ -0,0 +1,76 @@
|
||||
@import url('https://fonts.googleapis.com/css?family=Roboto');
|
||||
.quantity {
|
||||
position: absolute;
|
||||
bottom: 50px;
|
||||
}
|
||||
|
||||
input[type=number]::-webkit-inner-spin-button,
|
||||
input[type=number]::-webkit-outer-spin-button
|
||||
{
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
input[type=number]
|
||||
{
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
.quantity input {
|
||||
width: 45px;
|
||||
height: 42px;
|
||||
line-height: 1.65;
|
||||
float: left;
|
||||
display: block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
|
||||
.quantity input:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.quantity-nav {
|
||||
float: left;
|
||||
position: relative;
|
||||
height: 42px;
|
||||
}
|
||||
|
||||
.quantity-button {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
border-left: 1px solid #eee;
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
color: #333;
|
||||
font-size: 13px;
|
||||
font-family: "Trebuchet MS", Helvetica, sans-serif !important;
|
||||
line-height: 1.7;
|
||||
-webkit-transform: translateX(-100%);
|
||||
transform: translateX(-100%);
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
-o-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.quantity-button.quantity-up {
|
||||
position: absolute;
|
||||
height: 50%;
|
||||
top: 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.quantity-button.quantity-down {
|
||||
position: absolute;
|
||||
bottom: -1px;
|
||||
height: 50%;
|
||||
}
|
||||
|
||||
p{
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 24px;
|
||||
|
||||
}
|
||||
7
tutorials/jarvisMarsh/dot.js
Normal file
7
tutorials/jarvisMarsh/dot.js
Normal file
@@ -0,0 +1,7 @@
|
||||
class dot{
|
||||
|
||||
constructor(x, y){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
90
tutorials/jarvisMarsh/index.php
Normal file
90
tutorials/jarvisMarsh/index.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
if($_SERVER['HTTP_X_FORWARDED_PROTO'] !== 'https') {
|
||||
header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
|
||||
die();
|
||||
}
|
||||
?>
|
||||
|
||||
<DOCTYPE! html>
|
||||
<html>
|
||||
<title>Convex Hull</title>
|
||||
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css?family=Inconsolata" rel="stylesheet">
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
|
||||
<script
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.2.0/p5.min.js"
|
||||
integrity="sha512-b/htz6gIyFi3dwSoZ0Uv3cuv3Ony7EeKkacgrcVg8CMzu90n777qveu0PBcbZUA7TzyENGtU+qZRuFAkfqgyoQ=="
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.4/latest.js?config=AM_CHTML"></script>
|
||||
|
||||
<button id="backButton"
|
||||
onclick="window.location.href='../../tutorials.html'">Back</button>
|
||||
|
||||
|
||||
<head>
|
||||
<h1>Convex Hull - Jarvis' Marsh</h1>
|
||||
|
||||
<p>A convex hull is where a convex polygon is constructed to bound a set of points on a 2D plane. So, given all points in the set P, what is the smallest convex polygon we can construct that bounds all points in P.</p><br>
|
||||
<p>Pseudo code:</p>
|
||||
<div class="psudoCode">
|
||||
<ul>
|
||||
<li>Randomly place n points on a 2D plane.</li>
|
||||
<li>Find the leftmost point/the point with the smallest x value. Called `gamma`.</li>
|
||||
<li>Find the vector which is the <i>most left</i> which connects `gamma` to point q in set P.</li>
|
||||
<li>Store `gamma` and q in a list and repeat until q = `gamma`.</li>
|
||||
</div>
|
||||
<br>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<p>This canvas starts off by generating a set amount of points, placing them at random locations around the screen and adding them to an array. It then finds the index of the point in the array - `vertices[]` - with the lowest x value. It then loops through the array finding all vector's originating from `gamma`, <a>`vec(gammaP(i))`</a>.</p>
|
||||
<p>We then examine each vertex and evaluate which one is the <i>leftmost</i> from the perspective of point `gamma`. Say in this case `P(q)` is the leftmost vector so we push q onto the stack.</p><br>
|
||||
<p>Stack: `((q), (gamma))`</p><br>
|
||||
<p>We then repeat until we reach our starting point, which in this case means our starting point is the same as the next point (`q=gamma`)</p>
|
||||
<img class="examples" src="stepByStep.png">
|
||||
|
||||
<p>Now the question of how to calculate the <i>leftness</i> of a vector compared to another. We can use the <a>cross product</a> in 2D.</p>
|
||||
<p><a>`vecu times vecv = u`<sub>`x`</sub>`*v`<sub>`y`</sub>` - u`<sub>`y`</sub>`*v`<sub>`x`</sub></a> If the cross product is greater than 0, the vector is to the left. If it is equal to 0 then the vectors are colinear</p>
|
||||
|
||||
<p><b>Note:</b> to convert coordinates to a vector, find the difference between your origin point and your target point</p>
|
||||
|
||||
<div class="psudoCode">
|
||||
<a>for (var i = 0; i < vertices.length; i++){</a><br>
|
||||
<a> if (i != originPoint){</a><br>
|
||||
<a> x1 = vertices[recordLeftVector].x - vertices[originPoint].x;</a><br>
|
||||
<a> y1 = vertices[recordLeftVector].y - vertices[originPoint].y;</a><br><br>
|
||||
|
||||
<a> x2 = vertices[i].x - vertices[originPoint].x;</a><br>
|
||||
<a> y2 = vertices[i].y - vertices[originPoint].y;</a><br><br>
|
||||
|
||||
<a> if ((y2 * x1) - (y1 * x2) <= 0){</a><br>
|
||||
<a> recordLeftVector = i;</a><br>
|
||||
<a> }</a><br>
|
||||
|
||||
<a> }</a><br>
|
||||
<a>}</a><br>
|
||||
</div>
|
||||
|
||||
<p>Now we just have to add in a little piece of code at the bottom of this function to: increment the iteration number so we don't recurse until eternity, add our leftmost vector to the route stack and call the function again with the next point as a parameter.</p>
|
||||
|
||||
<div class="psudoCode">
|
||||
<a>if (originPoint != recordLeftVector && iterations < 75){</a><br>
|
||||
<a> route[iterations] = recordLeftVector;</a><br>
|
||||
<a> iterations += 1;</a><br>
|
||||
<a> findLeftLine(recordLeftVector);</a><br>
|
||||
|
||||
<a>}</a><br>
|
||||
</div><br><br>
|
||||
</body>
|
||||
|
||||
<script src="sketch.js"></script>
|
||||
<script src="dot.js"></script>
|
||||
|
||||
<!--https://www.youtube.com/watch?v=Vu84lmMzP2o<br>
|
||||
http://jeffe.cs.illinois.edu/teaching/compgeom/notes/01-convexhull.pdf</p>-->
|
||||
|
||||
</html>
|
||||
102
tutorials/jarvisMarsh/sketch.js
Normal file
102
tutorials/jarvisMarsh/sketch.js
Normal file
@@ -0,0 +1,102 @@
|
||||
var jarvisMarsh = function( p ){
|
||||
|
||||
const numPoints = 12;
|
||||
var vertices = [];
|
||||
var route = [];
|
||||
let iterations = 0;
|
||||
|
||||
p.setup = function(){
|
||||
p.createCanvas(750, 500);
|
||||
p.background(217);
|
||||
|
||||
button = p.createButton('Reset Button')
|
||||
button.mousePressed(p.resetSketch);
|
||||
|
||||
p.initValues();
|
||||
|
||||
}
|
||||
|
||||
p.draw = function(){}
|
||||
|
||||
|
||||
p.drawLine = function(){
|
||||
let r = p.findLeftPoint();
|
||||
route[iterations] = r;
|
||||
|
||||
p.findLeftLine(r);
|
||||
|
||||
p.stroke(215, 75, 75);
|
||||
p.strokeWeight(1);
|
||||
for (var i = 0; i < route.length - 1; i++){
|
||||
p.line(vertices[route[i]].x, vertices[route[i]].y, vertices[route[i+1]].x, vertices[route[i+1]].y)
|
||||
}
|
||||
}
|
||||
|
||||
p.findLeftLine = function(originPoint){
|
||||
var recordLeftVector = 1;
|
||||
let x1, x2, y1, y2;
|
||||
|
||||
for (var i = 0; i < vertices.length; i++){
|
||||
if (i != originPoint){
|
||||
x1 = vertices[recordLeftVector].x - vertices[originPoint].x;
|
||||
y1 = vertices[recordLeftVector].y - vertices[originPoint].y;
|
||||
|
||||
|
||||
|
||||
x2 = vertices[i].x - vertices[originPoint].x;
|
||||
y2 = vertices[i].y - vertices[originPoint].y;
|
||||
|
||||
//if the result if positive then vector u is left of vector v
|
||||
//where u and v are both vectors from the target point
|
||||
//If its equal to 0 then they're colinear. This is also good :)
|
||||
if ((y2 * x1) - (y1 * x2) <= 0){
|
||||
recordLeftVector = i;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (originPoint != recordLeftVector && iterations < 75){
|
||||
route[iterations] = recordLeftVector;
|
||||
iterations += 1;
|
||||
p.findLeftLine(recordLeftVector);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
p.findLeftPoint = function(){
|
||||
let recordLeft = 0;
|
||||
|
||||
for (var i = 0; i < vertices.length; i++){
|
||||
if (vertices[i].x < vertices[recordLeft].x){
|
||||
recordLeft = i;
|
||||
}
|
||||
}
|
||||
return recordLeft;
|
||||
}
|
||||
|
||||
p.resetSketch = function(){
|
||||
vertices.length = 0;
|
||||
p.clear()
|
||||
p.background(217);
|
||||
p.initValues();
|
||||
p.findLeftLine();
|
||||
p.draw();
|
||||
}
|
||||
|
||||
p.initValues = function(){
|
||||
p.stroke(0);
|
||||
p.strokeWeight(7);
|
||||
|
||||
var bufferx = p.width * 0.0625;
|
||||
var buffery = p.height * 0.0625;
|
||||
|
||||
for (var i = 0; i < numPoints; i++){
|
||||
vertices[i] = new dot(p.random(bufferx, p.width - bufferx), p.random(buffery, p.height - buffery));
|
||||
p.point(vertices[i].x, vertices[i].y);
|
||||
}
|
||||
iterations = 0;
|
||||
p.drawLine();
|
||||
}
|
||||
};
|
||||
var myp5 = new p5(jarvisMarsh, 'c1');
|
||||
BIN
tutorials/jarvisMarsh/stepByStep.png
Normal file
BIN
tutorials/jarvisMarsh/stepByStep.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
71
tutorials/jarvisMarsh/style.css
Normal file
71
tutorials/jarvisMarsh/style.css
Normal file
@@ -0,0 +1,71 @@
|
||||
h1{
|
||||
font-family: 'Roboto Condensed', sans-serif;
|
||||
margin: 0;
|
||||
padding: 0 0 15px 0;
|
||||
font-weight: 700;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
h2{
|
||||
font-family: 'Roboto Condensed', sans-serif;
|
||||
margin: 0;
|
||||
padding: 15px 0 15px 0;
|
||||
font-weight: 650;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
p{
|
||||
margin: 1px;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 19px;
|
||||
padding: 5px 0px 5px 0px;
|
||||
}
|
||||
|
||||
.psudoCode{
|
||||
font-family: 'Inconsolata', monospace;
|
||||
font-size: 19px;
|
||||
background-color: #D9D9D9;
|
||||
border: 1px solid;
|
||||
padding: 5px;
|
||||
box-shadow: 5px 5px #888888;
|
||||
}
|
||||
|
||||
a{
|
||||
font-family: 'Inconsolata', monospace;
|
||||
font-size: 19px;
|
||||
background-color: #D9D9D9;
|
||||
|
||||
}
|
||||
|
||||
button{
|
||||
background-color: white;
|
||||
border: 2px solid #555555;
|
||||
color: black;
|
||||
padding: 16px 32px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
margin: 4px 2px;
|
||||
-webkit-transition-duration: 0.4s; /* Safari */
|
||||
transition-duration: 0.4s;
|
||||
cursor: pointer;
|
||||
float:right;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #555555;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.examples{
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
||||
html{
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-ms-user-select: none; /* IE10+/Edge */
|
||||
user-select: none; /* Standard */
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
83
tutorials/summed_area/cStyle.scss
Normal file
83
tutorials/summed_area/cStyle.scss
Normal file
@@ -0,0 +1,83 @@
|
||||
@import "../nord.scss";
|
||||
@import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap');
|
||||
|
||||
$roboto-slab: 'Roboto', sans-serif;
|
||||
|
||||
html {
|
||||
background-color: $nord0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: $nord6;
|
||||
font-family: $roboto-slab;
|
||||
font-size: 48pt;
|
||||
}
|
||||
|
||||
p {
|
||||
color: $nord4;
|
||||
font-family: $roboto-slab;
|
||||
font-size: 16pt;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 14pt;
|
||||
border: 4px solid $nord13;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $nord4;
|
||||
font-family: $roboto-slab;
|
||||
font-size: 24pt;
|
||||
}
|
||||
|
||||
#subsetOutput {
|
||||
color: $nord8;
|
||||
}
|
||||
|
||||
#wholeOutput {
|
||||
color: $nord15;
|
||||
}
|
||||
|
||||
#tlOutput{
|
||||
color: $nord12;
|
||||
}
|
||||
|
||||
#tOutput{
|
||||
color: $nord13;
|
||||
}
|
||||
|
||||
#lOutput{
|
||||
color: $nord14;
|
||||
}
|
||||
|
||||
#sumDiv {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
button {
|
||||
display: inline-block;
|
||||
width: 25%;
|
||||
padding: 10px 25px;
|
||||
margin: 0.1em;
|
||||
border: 4px solid $nord8;
|
||||
box-sizing: border-box;
|
||||
text-decoration: none;
|
||||
font-family: 'Roboto',sans-serif;
|
||||
font-size: 14pt;
|
||||
color: $nord2;
|
||||
background-color: $nord6;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
transition: all 0.15s;
|
||||
|
||||
&:hover {
|
||||
background-color: $nord3;
|
||||
color: $nord6;
|
||||
border-color: $nord10;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border: 4px solid $nord8;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
278
tutorials/summed_area/script.js
Normal file
278
tutorials/summed_area/script.js
Normal file
@@ -0,0 +1,278 @@
|
||||
let myWidth = 800, myHeight = 800, offset = 5;
|
||||
let r, n = 10;
|
||||
|
||||
let t = [];
|
||||
let tl = null
|
||||
let br = null;
|
||||
|
||||
let showCount = false;
|
||||
let drawRectMode = 0;
|
||||
|
||||
let sOutput, wOutput, tlOutput, tOutput, lOutput;
|
||||
|
||||
|
||||
function setup()
|
||||
{
|
||||
createCanvas(myWidth + 2*offset, myHeight + 2*offset);
|
||||
r = myWidth / n;
|
||||
for (y = 0; y < n; y ++)
|
||||
{
|
||||
for (x = 0; x < n; x ++)
|
||||
{
|
||||
t.push(new cTile(x*r + offset, y*r + offset, r));
|
||||
}
|
||||
}
|
||||
sOutput = document.getElementById('subsetOutput');
|
||||
wOutput = document.getElementById('wholeOutput');
|
||||
tlOutput = document.getElementById('tlOutput');
|
||||
tOutput = document.getElementById('tOutput');
|
||||
lOutput = document.getElementById('lOutput');
|
||||
|
||||
}
|
||||
|
||||
function draw()
|
||||
{
|
||||
background(46, 52, 64);
|
||||
t.forEach(e => e.show());
|
||||
|
||||
stroke(156,212,228);
|
||||
noFill();
|
||||
strokeWeight(5);
|
||||
if (tl)
|
||||
{
|
||||
if (!br)
|
||||
{
|
||||
rect(tl.x, tl.y, tl.w);
|
||||
} else {
|
||||
rect(tl.x, tl.y, br.x - tl.x + r, br.y - tl.y + r);
|
||||
drawRect(drawRectMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function defineCount(br)
|
||||
{
|
||||
let cols = Math.floor(br.x/ r) + 1;
|
||||
let rows = Math.floor(br.y/ r) + 1;
|
||||
let c = 0;
|
||||
// let index = cols + n * rows;
|
||||
|
||||
for (y = 0; y < rows; y++) {
|
||||
for (x = 0; x < cols; x++){
|
||||
var i = x + n * y;
|
||||
if (t[i].s){
|
||||
c++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
function countSquares(tl, br)
|
||||
{
|
||||
let topLeft, left, top, whole;
|
||||
let tlx, tly, brx, bry;
|
||||
|
||||
tlx = Math.floor(tl.x / r);
|
||||
tly = Math.floor(tl.y / r);
|
||||
brx = Math.floor(br.x / r);
|
||||
bry = Math.floor(br.y / r);
|
||||
|
||||
topLeft = t[(tlx-1) + n * (tly-1)].count;
|
||||
left = t[(tlx-1) + n * bry ].count;
|
||||
top = t[brx + n * (tly-1)].count;
|
||||
whole = t[brx + n * bry ].count;
|
||||
|
||||
var s = whole + topLeft - top - left;
|
||||
if (s < 0)
|
||||
s = 0;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
function drawRect(n)
|
||||
{
|
||||
strokeWeight(5);
|
||||
noFill();
|
||||
|
||||
switch(n)
|
||||
{
|
||||
case 1:
|
||||
//whole
|
||||
stroke(180,142,173);
|
||||
fill('rgba(180, 142, 173, 0.05)');
|
||||
rect(offset, offset, br.x - offset + br.w, br.y - offset + br.w);
|
||||
break;
|
||||
case 2:
|
||||
//topleft
|
||||
stroke(208,135,112);
|
||||
fill('rgba(208,135,112, 0.05)');
|
||||
rect(offset, offset, tl.x - offset, tl.y - offset);
|
||||
break;
|
||||
case 3:
|
||||
//left
|
||||
stroke(163,190,140);
|
||||
fill('rgba(163,190,140, 0.05)');
|
||||
rect(offset, offset, tl.x - offset, br.y - offset + br.w);
|
||||
break;
|
||||
case 4:
|
||||
//top
|
||||
stroke(235,203,139);
|
||||
fill('rgba(235,203,139, 0.05)');
|
||||
rect(offset, offset, br.x - offset + br.w, tl.y - offset);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
strokeWeight(1);
|
||||
}
|
||||
|
||||
function showCountToggle(){
|
||||
showCount = !showCount;
|
||||
}
|
||||
|
||||
function toggleRectMode(){
|
||||
if (tl && br)
|
||||
{
|
||||
var tlx = Math.floor(tl.x / r);
|
||||
var tly = Math.floor(tl.y / r);
|
||||
var brx = Math.floor(br.x / r);
|
||||
var bry = Math.floor(br.y / r);
|
||||
|
||||
var topLeft = t[(tlx-1) + n * (tly-1)].count;
|
||||
var left = t[(tlx-1) + n * bry ].count;
|
||||
var top = t[brx + n * (tly-1)].count;
|
||||
var whole = t[brx + n * bry ].count;
|
||||
|
||||
if (drawRectMode == 0){
|
||||
wOutput.innerHTML = whole.toString(10);
|
||||
wOutput.style.fontWeight = "bold";
|
||||
drawRectMode ++;
|
||||
} else if (drawRectMode == 1){
|
||||
tlOutput.innerHTML = topLeft.toString(10);
|
||||
tlOutput.style.fontWeight = "bold";
|
||||
wOutput.style.fontWeight = "normal";
|
||||
drawRectMode ++;
|
||||
} else if (drawRectMode == 2){
|
||||
lOutput.innerHTML = left.toString(10);
|
||||
lOutput.style.fontWeight = "bold";
|
||||
tlOutput.style.fontWeight = "normal";
|
||||
drawRectMode ++;
|
||||
} else if (drawRectMode == 3) {
|
||||
tOutput.innerHTML = top.toString(10);
|
||||
tOutput.style.fontWeight = "bold";
|
||||
lOutput.style.fontWeight = "normal";
|
||||
drawRectMode ++;
|
||||
} else if (drawRectMode == 4) {
|
||||
tOutput.style.fontWeight = "normal";
|
||||
drawRectMode = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateSum()
|
||||
{
|
||||
var tlx = Math.floor(tl.x / r);
|
||||
var tly = Math.floor(tl.y / r);
|
||||
var brx = Math.floor(br.x / r);
|
||||
var bry = Math.floor(br.y / r);
|
||||
|
||||
var topLeft = t[(tlx-1) + n * (tly-1)].count;
|
||||
var left = t[(tlx-1) + n * bry ].count;
|
||||
var top = t[brx + n * (tly-1)].count;
|
||||
var whole = t[brx + n * bry ].count;
|
||||
|
||||
wOutput.innerHTML = whole.toString(10);
|
||||
tlOutput.innerHTML = topLeft.toString(10);
|
||||
lOutput.innerHTML = left.toString(10);
|
||||
tOutput.innerHTML = top.toString(10);
|
||||
sOutput.innerHTML = countSquares(tl,br).toString(10);
|
||||
}
|
||||
|
||||
function mouseClicked(event)
|
||||
{
|
||||
if (event.shiftKey)
|
||||
{
|
||||
if (mouseX > offset && mouseX < myWidth && mouseY > offset && mouseY < myHeight)
|
||||
{
|
||||
let col = Math.floor(mouseX / r);
|
||||
let row = Math.floor(mouseY / r);
|
||||
|
||||
if (tl != null && br != null)
|
||||
{
|
||||
tl = null;
|
||||
br = null;
|
||||
}
|
||||
|
||||
if (!tl)
|
||||
{
|
||||
tl = t[col + n * row];
|
||||
} else {
|
||||
br = t[col + n * row];
|
||||
}
|
||||
|
||||
if (tl != null && br != null)
|
||||
{
|
||||
updateSum();
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
if (mouseX > 0 && mouseX < myWidth && mouseY > 0 && mouseY < myHeight)
|
||||
{
|
||||
let col = Math.floor(mouseX / r);
|
||||
let row = Math.floor(mouseY / r);
|
||||
t[col + n * row].setState();
|
||||
|
||||
t.forEach(tile => tile.setCount(defineCount(tile)));
|
||||
|
||||
if (tl != null && br != null)
|
||||
{
|
||||
updateSum();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class cTile
|
||||
{
|
||||
constructor(_x, _y, _w)
|
||||
{
|
||||
this.x = _x;
|
||||
this.y = _y;
|
||||
this.s = false;
|
||||
this.w = _w;
|
||||
this.count = 0;
|
||||
}
|
||||
|
||||
show()
|
||||
{
|
||||
if (this.s)
|
||||
{
|
||||
fill(191,97,106);
|
||||
} else {
|
||||
noFill();
|
||||
}
|
||||
stroke(236,239,244);
|
||||
strokeWeight(1);
|
||||
rect(this.x, this.y, this.w, this.w);
|
||||
if (showCount)
|
||||
{
|
||||
textSize(18);
|
||||
stroke(255);
|
||||
fill(255);
|
||||
text(this.count.toString(10), this.x + this.w/2 - 3, this.y + this.w/2 + 5);
|
||||
}
|
||||
}
|
||||
|
||||
setState()
|
||||
{
|
||||
this.s = !this.s;
|
||||
}
|
||||
|
||||
setCount(c)
|
||||
{
|
||||
this.count = c;
|
||||
}
|
||||
};
|
||||
68
tutorials/summed_area/style.css
Normal file
68
tutorials/summed_area/style.css
Normal file
@@ -0,0 +1,68 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Roboto&display=swap");
|
||||
html {
|
||||
background-color: #2e3440; }
|
||||
|
||||
h1 {
|
||||
color: #eceff4;
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-size: 48pt; }
|
||||
|
||||
p {
|
||||
color: #d8dee9;
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-size: 16pt; }
|
||||
|
||||
code {
|
||||
font-size: 14pt;
|
||||
border: 4px solid #ebcb8b; }
|
||||
|
||||
a {
|
||||
color: #d8dee9;
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-size: 24pt; }
|
||||
|
||||
#subsetOutput {
|
||||
color: #88c0d0; }
|
||||
|
||||
#wholeOutput {
|
||||
color: #b48ead; }
|
||||
|
||||
#tlOutput {
|
||||
color: #d08770; }
|
||||
|
||||
#tOutput {
|
||||
color: #ebcb8b; }
|
||||
|
||||
#lOutput {
|
||||
color: #a3be8c; }
|
||||
|
||||
#sumDiv {
|
||||
text-align: center; }
|
||||
|
||||
button {
|
||||
display: inline-block;
|
||||
width: 25%;
|
||||
padding: 10px 25px;
|
||||
margin: 0.1em;
|
||||
border: 4px solid #88c0d0;
|
||||
box-sizing: border-box;
|
||||
text-decoration: none;
|
||||
font-family: 'Roboto',sans-serif;
|
||||
font-size: 14pt;
|
||||
color: #434c5e;
|
||||
background-color: #eceff4;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
transition: all 0.15s; }
|
||||
button:hover {
|
||||
background-color: #4c566a;
|
||||
color: #eceff4;
|
||||
border-color: #5e81ac; }
|
||||
button:focus {
|
||||
border: 4px solid #88c0d0;
|
||||
box-sizing: border-box; }
|
||||
|
||||
#chillin {
|
||||
display: none; }
|
||||
|
||||
/*# sourceMappingURL=style.css.map */
|
||||
7
tutorials/summed_area/style.css.map
Normal file
7
tutorials/summed_area/style.css.map
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"mappings": "AACQ,2EAAmE;AAI3E,IAAK;EACD,gBAAgB,EC+BZ,OAAO;;AD5Bf,EAAG;EACC,KAAK,ECuID,OAAO;EDtIX,WAAW,EARD,oBAAoB;EAS9B,SAAS,EAAE,IAAI;;AAGnB,CAAE;EACE,KAAK,EC2GD,OAAO;ED1GX,WAAW,EAdD,oBAAoB;EAe9B,SAAS,EAAE,IAAI;;AAGnB,IAAK;EACD,SAAS,EAAE,IAAI;EACf,MAAM,EAAE,iBAAiB;;AAG7B,CAAE;EACE,KAAK,ECgGD,OAAO;ED/FX,WAAW,EAzBD,oBAAoB;EA0B9B,SAAS,EAAE,IAAI;;AAGnB,aAAc;EACV,KAAK,ECuID,OAAO;;ADpIf,YAAa;EACT,KAAK,ECqMA,OAAO;;ADlMhB,SAAS;EACL,KAAK,ECoKA,OAAO;;ADjKhB,QAAQ;EACJ,KAAK,EC0KA,OAAO;;ADvKhB,QAAQ;EACJ,KAAK,ECgLA,OAAO;;AD7KhB,OAAQ;EACJ,UAAU,EAAE,MAAM;;AAGtB,MAAO;EACH,OAAO,EAAE,YAAY;EACrB,KAAK,EAAE,GAAG;EACV,OAAO,EAAE,SAAS;EAClB,MAAM,EAAE,KAAK;EACb,MAAM,EAAE,iBAAgB;EACxB,UAAU,EAAE,UAAU;EACtB,eAAe,EAAE,IAAI;EACrB,WAAW,EAAE,mBAAmB;EAChC,SAAS,EAAE,IAAI;EACf,KAAK,ECMD,OAAO;EDLX,gBAAgB,EC8EZ,OAAO;ED7EX,UAAU,EAAE,MAAM;EAClB,QAAQ,EAAE,QAAQ;EAClB,UAAU,EAAE,SAAS;EAErB,YAAQ;IACJ,gBAAgB,EC+BhB,OAAO;ID9BP,KAAK,ECuEL,OAAO;IDtEP,YAAY,ECgHX,OAAO;ED7GZ,YAAQ;IACJ,MAAM,EAAE,iBAAgB;IACxB,UAAU,EAAE,UAAU;;AAI9B,QAAS;EACL,OAAO,EAAE,IAAI",
|
||||
"sources": ["cStyle.scss","../nord.scss"],
|
||||
"names": [],
|
||||
"file": "style.css"
|
||||
}
|
||||
106
tutorials/summed_area/summed-area.html
Normal file
106
tutorials/summed_area/summed-area.html
Normal file
@@ -0,0 +1,106 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<script src="../../syntax_styles/highlight.pack.js"></script>
|
||||
<link rel="stylesheet" href="../styles/atom-one-dark.css" />
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.2.0/p5.min.js"
|
||||
integrity="sha512-b/htz6gIyFi3dwSoZ0Uv3cuv3Ony7EeKkacgrcVg8CMzu90n777qveu0PBcbZUA7TzyENGtU+qZRuFAkfqgyoQ=="
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="script.js"></script>
|
||||
<script>
|
||||
hljs.initHighlightingOnLoad();
|
||||
</script>
|
||||
|
||||
<head>
|
||||
<h1>Summed-area Table</h1>
|
||||
<p>
|
||||
A Summed-area table is a data structure and algorithm for quickly and
|
||||
efficiently generating the sum of values in a rectangular grid. In image
|
||||
processing it is also known as an integral image.
|
||||
</p>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>
|
||||
To generate a summed-area table, each cell is given a value which is the
|
||||
sum of all values in a rectangular subset where the top left corner is the
|
||||
first cell in the grid and where the bottom right cell is the cell you're
|
||||
calculating the value for (assumuing the array starts top left).
|
||||
</p>
|
||||
<pre>
|
||||
<code class="js">
|
||||
function defineCount(br)
|
||||
{
|
||||
let c = 0;
|
||||
//where br is the cell we're calculating
|
||||
//br.x is the column number
|
||||
//br.y is the row number
|
||||
for (y = 0; y <= br.y; y++) {
|
||||
//loop through all rows above br (and the row br is on)
|
||||
for (x = 0; x <= br.x; x++) {
|
||||
//loop through all columns to the left of br (and br column)
|
||||
var i = x + n * y; //convert 2d array format to 1d index
|
||||
|
||||
if (t[i].s){ //if cell[i].status == true then increase count
|
||||
c++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Now each cell has its own count value. This isn't much help on it's own,
|
||||
however we can use it to calculate the sum value of any subset of the grid
|
||||
by interrogating 4 cells and performing only 4 additions. NOTE: we have to
|
||||
add the 'top left' region as it as been subtracted twice.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
<code class="js">
|
||||
function countSquares(tl, br) {
|
||||
//where tl and br are the top left and bottom right cell respectively
|
||||
|
||||
let topLeft, left, top, whole;
|
||||
//these are the four cells to interrogate
|
||||
|
||||
//1d index = x + #rows * y
|
||||
topLeft = t[(tl.x-1) + n * (tl.y-1)].count;
|
||||
left = t[(tl.x-1) + n * bry ].count;
|
||||
top = t[br.x + n * (tl.y-1)].count;
|
||||
whole = t[br.x + n * br.y ].count;
|
||||
|
||||
return whole + topLeft - top - left;
|
||||
}
|
||||
</code>
|
||||
</pre>
|
||||
<p><b>Click</b> a sqaure on the grid to turn it on or off.</p>
|
||||
<p><b>Shift+Click</b> two square to define a rectangle.</p>
|
||||
<p>
|
||||
Press the <b>show count</b> button to display the sum value of each cell
|
||||
(in this example a cell's value can only be 0 or 1).
|
||||
</p>
|
||||
<p>
|
||||
Press <b>show rectangles</b> to see the different regions simplified and
|
||||
see how
|
||||
it changes the sum below.
|
||||
</p>
|
||||
<div id="sumDiv">
|
||||
<a id="subsetOutput">0</a>
|
||||
<a> = </a>
|
||||
<a id="wholeOutput"> 0</a>
|
||||
<a> + </a>
|
||||
<a id="tlOutput"> 0</a>
|
||||
<a> - </a>
|
||||
<a id="lOutput"> 0</a>
|
||||
<a> - </a>
|
||||
<a id="tOutput"> 0</a>
|
||||
</div>
|
||||
|
||||
<button onclick="showCountToggle()">Show count</button>
|
||||
<button onclick="toggleRectMode()">Show rectangles</button>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
BIN
tutorials/terrainGen/Jagged_Terrain.jpg
Normal file
BIN
tutorials/terrainGen/Jagged_Terrain.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
8
tutorials/terrainGen/dot.js
Normal file
8
tutorials/terrainGen/dot.js
Normal file
@@ -0,0 +1,8 @@
|
||||
class dot{
|
||||
|
||||
constructor(x, y){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
}
|
||||
107
tutorials/terrainGen/index.html
Normal file
107
tutorials/terrainGen/index.html
Normal file
@@ -0,0 +1,107 @@
|
||||
<html>
|
||||
<title>Midpoint Displacement</title>
|
||||
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto"
|
||||
rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css?family=Inconsolata"
|
||||
rel="stylesheet">
|
||||
<link rel"shortcut icon" type"image/ico" href"/Media/iconImage.ico">
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
|
||||
<button id="backButton"
|
||||
onclick="window.location.href='../../tutorials.html'">Back</button>
|
||||
|
||||
<head>
|
||||
|
||||
<h1>Midpoint Displacement</h1>
|
||||
|
||||
<p>Midpoint displacement is a common way to generate terrains in video
|
||||
games. Instead of manually creating a stage you can make an
|
||||
algorithm create one based off randomness. This is faster to
|
||||
calculate than a perlin noise stage which is why it can be found
|
||||
frequently in older video game titles.</p><br>
|
||||
<p>Psudo code:</p>
|
||||
<div class="psudoCode">
|
||||
<ul>
|
||||
<li>Create two points.</li>
|
||||
<li>Find the center x coordinate.</li>
|
||||
<li>Create a new point in between the two first points with some
|
||||
offset in the y coorindate.</li>
|
||||
<li>Repeat. (can be recursive)</li>
|
||||
</div>
|
||||
<br>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<p>This canvas starts off with two points in an array called
|
||||
<a>points[]</a>, one at <a>height/2</a> and one at
|
||||
<a>math.random(-vertical_displacement, vertical_displacement)</a>.
|
||||
The center point is found and a new point is made and added to the
|
||||
array. This new point is offset on the y-axis by an amount however
|
||||
we can't use the same <a>vertical_displacement</a> as we did before
|
||||
as we'd end up with a very jagged terrain.
|
||||
</p>
|
||||
<img class="examples" src="Jagged_Terrain.jpg">
|
||||
|
||||
<p>Instead we have to iterate our variable -
|
||||
<a>vertical_displacement</a> - based on the iteration number of the
|
||||
sketch, in order to generate a smooth stage/terrain.
|
||||
</p>
|
||||
|
||||
<div class="psudoCode">
|
||||
<a>vertical_displacement = points[0].y + points[1].y / 2;</a><br>
|
||||
<a>vertical_displacement *= 2 ** (-roughness);</a>
|
||||
</div>
|
||||
|
||||
<p>The top formula is the inital value. The second is applied very
|
||||
iteration of the sketch.</p>
|
||||
<p>Right now we can add one point to the list, time to add several! This
|
||||
means for loops. But before we can loop through our array how do we
|
||||
know which point should connect to which? We need to sort the array
|
||||
based on x value, so that the points read off from left to right.
|
||||
</p>
|
||||
<p>I just used a bubble sort for simplicity however it's worth pointing
|
||||
out that the number of points grows exponetially - <a>O(2^n)</a>
|
||||
where n = iterations - so after just 10 iterations <a>points.length
|
||||
= 1024</a>. A merge sort could be used to speed up computation
|
||||
times.</p>
|
||||
<p>Now we can use a for loop to go through our array and compare the ith
|
||||
item to the (i+1)th item, and then update <a>iterations</a> and
|
||||
<a>vertical_displacement</a>.
|
||||
</p>
|
||||
|
||||
<div class="psudoCode">
|
||||
<a>var pLength = points.length;</a><br><br>
|
||||
|
||||
<a> for (var i = 0; i < pLength - 1; i++){</a><br>
|
||||
|
||||
<a> var midX = (points[i].x + points[i +
|
||||
1].x) / 2;</a><br>
|
||||
<a> var midY = (points[i].y + points[i +
|
||||
1].y) / 2;</a><br>
|
||||
<a> midY += random(-vertical_displacement,
|
||||
vertical_displacement);</a><br>
|
||||
|
||||
<a> points[pLength + i] = new point(midX,
|
||||
midY);</a><br>
|
||||
<a> }</a><br><br>
|
||||
|
||||
<a>iterations += 1;</a><br>
|
||||
<a>vertical_displacement *= 2 ** (-roughness);</a><br>
|
||||
|
||||
<a>bubbleSort();</a><br>
|
||||
</div>
|
||||
<h2>NOTE: Do not use points.length to define your for loop as we are
|
||||
increasing the length of the array within the loop</h2>
|
||||
<!--<span id="c1"></span>-->
|
||||
</body>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.2.0/p5.min.js"
|
||||
integrity="sha512-b/htz6gIyFi3dwSoZ0Uv3cuv3Ony7EeKkacgrcVg8CMzu90n777qveu0PBcbZUA7TzyENGtU+qZRuFAkfqgyoQ=="
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="sketch.js"></script>
|
||||
<script src="dot.js"></script>
|
||||
|
||||
|
||||
</html>
|
||||
90
tutorials/terrainGen/sketch.js
Normal file
90
tutorials/terrainGen/sketch.js
Normal file
@@ -0,0 +1,90 @@
|
||||
var MidDisplacement = function( p ){
|
||||
|
||||
var points = [];
|
||||
var iterations = 1;
|
||||
const roughness = 1;
|
||||
var vertical_displacement;
|
||||
let button;
|
||||
|
||||
p.setup = function(){
|
||||
p.createCanvas(800, 500);
|
||||
p.frameRate(2);
|
||||
p.background(217);
|
||||
|
||||
button = p.createButton('Reset Button')
|
||||
//button.position(p.width, p.height);
|
||||
button.mousePressed(p.resetSketch);
|
||||
p.initValues();
|
||||
|
||||
}
|
||||
|
||||
p.draw = function(){}
|
||||
|
||||
p.bubbleSort = function() {
|
||||
var length = points.length;
|
||||
//Number of passes
|
||||
for (var i = 0; i < length; i++) {
|
||||
//Notice that j < (length - i)
|
||||
for (var j = 0; j < (length - i - 1); j++) {
|
||||
//Compare the x values
|
||||
if(points[j].x > points[j+1].x) {
|
||||
//Swap the numbers
|
||||
var tmp = points[j]; //Temporary variable to hold the current number
|
||||
points[j] = points[j+1]; //Replace current number with adjacent number
|
||||
points[j+1] = tmp; //Replace adjacent number with current number
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p.mouseClicked = function(){
|
||||
if (iterations < 11){
|
||||
var pLength = points.length;
|
||||
|
||||
for (var i = 0; i < pLength - 1; i++){
|
||||
|
||||
var midX = (points[i].x + points[i + 1].x) / 2;
|
||||
var midY = (points[i].y + points[i + 1].y) / 2;
|
||||
midY += p.random(-vertical_displacement, vertical_displacement);
|
||||
|
||||
points[pLength + i] = new dot(midX, midY);
|
||||
}
|
||||
|
||||
iterations += 1;
|
||||
vertical_displacement *= 2 ** (-roughness);
|
||||
|
||||
p.bubbleSort();
|
||||
|
||||
p.clear();
|
||||
p.background(215);
|
||||
|
||||
for (var i = 0; i < pLength; i++){
|
||||
p.strokeWeight(4);
|
||||
p.stroke(0);
|
||||
p.point(points[i].x * 2, points[i].y);
|
||||
|
||||
p.strokeWeight(1);
|
||||
p.stroke(215, 15, 15);
|
||||
p.line(points[i].x * 2, points[i].y, points[i+1].x * 2, points[i+1].y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p.initValues = function(){
|
||||
points[0] = new dot(0, p.height/2);
|
||||
points[1] = new dot(p.width, p.height/2);
|
||||
|
||||
vertical_displacement = points[0].y + points[1].y / 2;
|
||||
vertical_displacement *= 2 ** (-roughness);
|
||||
|
||||
iterations = 1;
|
||||
}
|
||||
|
||||
p.resetSketch = function(){
|
||||
points.length = 0;
|
||||
p.clear()
|
||||
p.background(217);
|
||||
p.initValues();
|
||||
}
|
||||
};
|
||||
var myp5 = new p5(MidDisplacement, 'c1');
|
||||
71
tutorials/terrainGen/style.css
Normal file
71
tutorials/terrainGen/style.css
Normal file
@@ -0,0 +1,71 @@
|
||||
h1{
|
||||
font-family: 'Roboto Condensed', sans-serif;
|
||||
margin: 0;
|
||||
padding: 0 0 15px 0;
|
||||
font-weight: 700;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
h2{
|
||||
font-family: 'Roboto Condensed', sans-serif;
|
||||
margin: 0;
|
||||
padding: 15px 0 15px 0;
|
||||
font-weight: 650;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
p{
|
||||
margin: 1px;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 19px;
|
||||
padding: 5px 0px 5px 0px;
|
||||
}
|
||||
|
||||
.psudoCode{
|
||||
font-family: 'Inconsolata', monospace;
|
||||
font-size: 19px;
|
||||
background-color: #D9D9D9;
|
||||
border: 1px solid;
|
||||
padding: 5px;
|
||||
box-shadow: 5px 5px #888888;
|
||||
}
|
||||
|
||||
a{
|
||||
font-family: 'Inconsolata', monospace;
|
||||
font-size: 19px;
|
||||
background-color: #D9D9D9;
|
||||
|
||||
}
|
||||
|
||||
button{
|
||||
background-color: white;
|
||||
border: 2px solid #555555;
|
||||
color: black;
|
||||
padding: 16px 32px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
margin: 4px 2px;
|
||||
-webkit-transition-duration: 0.4s; /* Safari */
|
||||
transition-duration: 0.4s;
|
||||
cursor: pointer;
|
||||
float:right;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #555555;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.examples{
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
||||
html{
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-ms-user-select: none; /* IE10+/Edge */
|
||||
user-select: none; /* Standard */
|
||||
}
|
||||
Reference in New Issue
Block a user