├── README.md └── sierpinski.js /README.md: -------------------------------------------------------------------------------- 1 | ## About 2 | A stochastic method to generate an approximation to the Sierpinski triangle. Made using [p5.js](https://p5js.org). 3 | 4 | ## Demo 5 | [Imgur link](https://i.imgur.com/1Svgp0G.mp4) 6 | 7 | ## Algorithm 8 | 1. Pick a random point p inside the triangle ([algorithm](https://blogs.sas.com/content/iml/2020/10/19/random-points-in-triangle.html)). 9 | 2. Draw it. 10 | 3. Pick a random vertex of the triangle and find its midpoint with p. Set p to that midpoint. 11 | 4. Go to step 2. 12 | 13 | ## Why it works 14 | Not sure. Need to figure this one out. 15 | -------------------------------------------------------------------------------- /sierpinski.js: -------------------------------------------------------------------------------- 1 | // Globals. 2 | const gWidth = 400; // canvas width = height 3 | const gSide = 360; // triangle side length 4 | let gTriangle; // enclosing triangle 5 | let gPoint; // point that gets drawn then updated in each iteration 6 | 7 | // Set up the canvas, the triangle and a seed point. 8 | function setup() { 9 | createCanvas(gWidth, gWidth); 10 | background(0); 11 | 12 | gTriangle = setupTriangle(); 13 | drawTriangle(); 14 | 15 | gPoint = randomPoint(); 16 | } 17 | 18 | // Return the vertices of the triangle. 19 | // The triangle is equilateral, points upward and 20 | // its base is centred along the x-axis of the canvas. 21 | // Its offset from the bottom of the canvas is equal 22 | // to its offset from the left side of the canvas. 23 | function setupTriangle() { 24 | const offset = (gWidth-gSide)/2; 25 | return [ 26 | createVector(offset, gSide+offset), 27 | createVector(gSide+offset, gSide+offset), 28 | createVector((gSide+offset)/2, gWidth-offset-gSide*0.866) // 0.866 = sqrt(3)/2 29 | ]; 30 | } 31 | 32 | // Draw the triangle. 33 | function drawTriangle() { 34 | noFill(); 35 | stroke('white'); 36 | strokeWeight(2); 37 | triangle( 38 | gTriangle[0].x, gTriangle[0].y, 39 | gTriangle[1].x, gTriangle[1].y, 40 | gTriangle[2].x, gTriangle[2].y, 41 | ); 42 | } 43 | 44 | // Draw a point at (x, y). 45 | function drawMark(x, y) { 46 | noStroke(); 47 | stroke('white'); 48 | strokeWeight(2); 49 | point(x, y); 50 | } 51 | 52 | // Generate a random point in the triangle. 53 | // Algorithm: https://blogs.sas.com/content/iml/2020/10/19/random-points-in-triangle.html 54 | function randomPoint() { 55 | const [p1, p2, p3] = gTriangle; 56 | const p2_p1 = p5.Vector.sub(p2, p1); 57 | const p3_p1 = p5.Vector.sub(p3, p1); 58 | let [u1, u2] = [random(), random()]; 59 | if (u1 + u2 > 1) { 60 | [u1, u2] = [1 - u1, 1 - u2]; 61 | } 62 | return p5.Vector.add( 63 | p1, 64 | p5.Vector.add( 65 | p5.Vector.mult(p2_p1, u1), 66 | p5.Vector.mult(p3_p1, u2) 67 | ) 68 | ); 69 | } 70 | 71 | // Run a step of the algorithm. 72 | // See README.md for the algorithm. 73 | function step() { 74 | drawMark(gPoint.x, gPoint.y); 75 | let randomVertex = gTriangle[floor(random()*3)]; 76 | gPoint = p5.Vector.div( 77 | p5.Vector.add(gPoint, randomVertex), 78 | 2 79 | ); 80 | } 81 | 82 | // Main rendering function, called in a loop. 83 | function draw() { 84 | step(); 85 | } 86 | --------------------------------------------------------------------------------