Add projects and tutorials
This commit is contained in:
7
tutorials/convex_hull/dot.js
Normal file
7
tutorials/convex_hull/dot.js
Normal file
@@ -0,0 +1,7 @@
|
||||
class dot{
|
||||
|
||||
constructor(x, y){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
95
tutorials/convex_hull/index.html
Normal file
95
tutorials/convex_hull/index.html
Normal file
@@ -0,0 +1,95 @@
|
||||
<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'">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/convex_hull/sketch.js
Normal file
102
tutorials/convex_hull/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/convex_hull/stepByStep.png
Normal file
BIN
tutorials/convex_hull/stepByStep.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
71
tutorials/convex_hull/style.css
Normal file
71
tutorials/convex_hull/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