Merry Everything from eSeeCode

×

Error message

Deprecated function: The each() function is deprecated. This message will be suppressed on further calls in menu_set_active_trail() (line 2396 of /homepages/8/d424690692/htdocs/clickandbuilds/Drupal/Drupal7/includes/menu.inc).

We are getting close to the end of the year and we want to wish you happy holidays! To do so we have created this cute gif, and we will explain how we did it step by step.

Koch Snowflake
The gif we wanted to present had to satisfy different criteria:
  • Had to be Holiday theme related and beautiful.
  • Had to be easy to program, as we wanted anyone to be able to do it.
  • Had to have some interesting programming technique behind it.
  • Had to have some mathematics behind it.

We though about making a tutorial on how to represent 3D into a 2D screen, but after a while we decided to go for the Koch Fractal as a snowflake. The Koch Fractal is one of the most known fractals, and it was created in 1904 by Helge von Koch. A fractal is a self-similar figure, that means a figure that repeats itself indefenitely.

The algorithm we would do was quite simple:

  1. Build Koch Fractal
  2. Make an animation where the fractal would change size
  3. Make it change its color as it changes size
  4. Mixing everything
  5. Final details

As it can be read in the wikipedia article Koch fractal can be constructed as follows:

  1. divide the line segment into three segments of equal length.
  2. draw an equilateral triangle that has the middle segment from step 1 as its base and points outward.
  3. remove the line segment that is the base of the triangle from step2.

The Koch fractal appears when you repeat this process for each new segment infinitely. In our approximation we will repeat it a fixed number of times.

If we imagine we have a segment of length 100, this process can be translated with the following code:


        forward (100/3)
	turnLeft(60)
	forward (100/3)
	turnRight(120)
        forward(100/3)
	turnLeft(60)
        forward (100/3)

Because we cannot repeat this process an infinite number of times, we will use recursive functions to repeat the process. A recursive function is a function that calls itself. Similarly to induction, in every recursive function we have to write a base case, which will be the condition to STOP. In this example we will use the idea of depth of the recursion. No depth (depth=0) will mean that we just want to draw that segment. The final code will be:

      function koch_snowflake_side(length, depth) {
	   if (depth == 0) {
		forward(length)
	  	return
	  }
	  koch_snowflake_side(length / 3, depth - 1)
	  turnLeft(60)
	  koch_snowflake_side(length / 3, depth - 1)
	  turnRight(120)
	  koch_snowflake_side(length / 3, depth - 1)
	  turnLeft(60)
	  koch_snowflake_side(length / 3, depth - 1)
      }
      goTo(-200, 0)
      koch_snowflake_side(400, 4)
 
We can see that the function calls itself 4 times (one for each forward in the initial definition). The return statement in the base case will make sure not to continue with that specific run of the function (avoiding then the recursive calls).

We wanted the size of the picture to increase during the runs. To make the explanation simpler we will use a triangle instead of the Koch. Simplifying parts of your code is an interesting technique to make sure you can solve the smaller problem before taking on the challenge.

One difficulty that you can find is the fact that you want the equilateral triangle to be centered. To do so we have to use the goTo instruction. The coordinate of the left corner will be defined by what we consider the "center" of the triangle. There are many important points on triangle such as, orthocenter, incenter, circumcenter and also centroid. Check this article for more information. Although we just want to know the coordinates of the centroid, in the case of the equilateral triangle all the points are the same. This will mean that using the propety of the centroid (that it's in a 2:1 ratio) we can just setup the coordinates to: goTo(-width / 2, -width * getSquareRoot(3) / 6) where width is the size of the side.

There are many ideas behind the growing of the triangle.

  • First of all we need to know the width of the side. We can use a repeat statement to build each triangle, and inside use the repeatCount variable to define our width. Note that the repeatCount inside a repeat statement starts at 0 and finishes one unit before the conter. In this case it will go from 0 to 5. This means that width has to be + 1
  • Before drawing the triangle we will use clean() to erase the content of the current layer.
  • After drawing the triangle we will use snapshot() to save the image in the layer.
  • Before doing the animateLayers() we will have to pop the first canvas (number 0) using pop(0) statement. The reason is that this is the canvas we are using at that moment and it contains the same image as canvas 6. This would mean that the animation would have an extra canvas.

If we follow this steps the code will look like this:

						
   repeat (6) {
	clean()
	var width = 50 * (repeatCount + 1)
	goTo(-width / 2, -width * getSquareRoot(3) / 6)
	repeat (3) {
	 	forward(width)
	   	turnLeft(120)
	}
	snapshot()
  }
  pop(0)
  animateLayers()
						
					

Our animation should change of color when it's being executed. Chosing colours can be a difficult step, but the interesting part is how to use them inside a loop.

First of all you can chose from this list of colors. You can either use the color name, or the HEX value. In the drag mode, when editing the instruction setColor() you can use a color selector.

The parameter in setColor() can be the HEX value of a color (prefixed by #) or its name. You can also use the RGB value of a color if you use the function getRGB(r,g,b). You can always use getRandomColor() if you want a random color.

To be able to change colors inside a loop, such as repeat, we will have to use a vector. A vector is a data structure that allows us to store multiple values. An empty vector is declared like this: var Vect = [] in this case we don't want the vector to be empty but to contain our colors. To access a value inside a vector you can use Vec[2] where Vec would be the name of your variable, and 2 the position of the value you want to access. Note that the positions start at 0.

For example, if the vector is defined like this: var colors = ["Red", "Green", "Blue", "Orange"] then colors[2] would give us "Blue"

As in the previous step we will use the repeatCount variable to go over the values of our vector.

As an example we will paint the side of a square with 4 different colors:

						
 var colors = ["Red", "Green", "Blue", "Orange"]
 repeat (4) {
	setColor(colors[repeatCount])
	forward (100)
	turnLeft (90)
 }
						
					

When mixing everything we have to start by defining our functions. In this case it will be the koch_snoflake_side from step 1. Instead of moving forward in example 2 we will use this function, so it will create a complete snowflake. We will also add the lines to change colors inside the repeat. For this example we have chosen Dark colors from the list in Step 2. After trying for a while we decided to create another variable to control the depth of the fractal. We cannot use the repeatCount from the triangle because it will only go from 0 to 2.

The final code before the details is as follows:

						
// Recursive Koch fractal function
function koch_snowflake_side(length, depth) {
	if (depth == 0) {
		forward(length)
		return
	}
	koch_snowflake_side(length / 3, depth - 1)
	turnLeft(60)
	koch_snowflake_side(length / 3, depth - 1)
	turnRight(120)
	koch_snowflake_side(length / 3, depth - 1)
	turnLeft(60)
	koch_snowflake_side(length / 3, depth - 1)
}

// Main code
var colors = ["DarkSalmon", "DarkSeaGreen", "DarkKhaki", "DarkOrange", "DarkOrchid", "DarkRed"]
repeat (6) {
	clean()
	var width = 50 * (repeatCount + 1)
	var depth = repeatCount
	setColor(colors[repeatCount])
	goTo(-width / 2, -width * getSquareRoot(3) / 6)
	repeat (3) {
		koch_snowflake_side(width, depth)
		turnLeft(120)
	}
	snapshot()
}
pop(0)
animateLayers()
						
					

If you execute the previous code you will see it doesn't quite do what we wanted. This is because the koch_snowflake_side () creates the curve inside the triangle and not outside. This can be changed by either creating the triangle clockwise or changing all the angles. The corrected code will be:

						
// Recursive Koch fractal function
function koch_snowflake_side(length, depth) {
	if (depth == 0) {
		forward(length)
		return
	}
	koch_snowflake_side(length / 3, depth - 1)
	turnRight(60)
	koch_snowflake_side(length / 3, depth - 1)
	turnLeft(120)
	koch_snowflake_side(length / 3, depth - 1)
	turnRight(60)
	koch_snowflake_side(length / 3, depth - 1)
}

// Main code
var colors = ["DarkSalmon", "DarkSeaGreen", "DarkKhaki", "DarkOrange", "DarkOrchid", "DarkRed"]
repeat (6) {
	clean()
	var width = 50 * (repeatCount + 1)
	var depth = repeatCount
	setColor(colors[repeatCount])
	goTo(-width / 2, -width * getSquareRoot(3) / 6)
	repeat (3) {
		koch_snowflake_side(width, depth)
		turnLeft(120)
	}
	snapshot()
}
pop(0)
animateLayers()
						
					

We want to add some extra details to our code.

First of all we will use the beginShape() and endShape() instructions to give a little more color to our gif. This instructions work very well with the forward instructions we have been using, and will fill our figures with the current color.

As an example you can see how it works with this square.

						
beginShape()
line(0,100)
line(100,100)
line(100,0)
line(0,0)
endShape()
					
					

We will use this to have a non white background, and to have our snowflakes more colorful.

Another thing we wanted to do was to write a message on the screen. This can be done with the writeAt instruction. In this instruction you can state the message and give the coordinates and the direction. With setsize(12) we can make the message bigger.

Finaly we must add snapshot() instructions after the greetings to make sure they are in our animation. Because animateLayers goes through all the layers (and shows them the same amount of time) we will do multiple snapshot() to increase the duration of that frame.

The final code putting everything together will be:

						
// Recursive Koch fractal function
function koch_snowflake_side(length, depth) {
	if (depth == 0) {
		forward(length)
		return
	}
	koch_snowflake_side(length / 3, depth - 1)
	turnRight(60)
	koch_snowflake_side(length / 3, depth - 1)
	turnLeft(120)
	koch_snowflake_side(length / 3, depth - 1)
	turnRight(60)
	koch_snowflake_side(length / 3, depth - 1)
}

// Add holidays greetings
function greetHolidays() {
	setBold()
	setColor("Plum")
	setSize(12)
	writeAt("Merry everything", -85, -27, 25)
	writeAt("& Happy always!", -55, -52, 25)
}

function background() {
	goTo(-200, 200)
	setColor("SteelBlue")
	beginShape()
	line(200, 200)
	line(200, -200)
	line(-200, -200)
	line(-200, 200)
	endShape()
}

// Main code
var colors = ["DarkSalmon", "DarkSeaGreen", "DarkKhaki", "DarkOrange", "DarkOrchid", "DarkRed"]
repeat (6) {
	clean()
	background()
	var width = 50 * (repeatCount + 1)
	var depth = repeatCount
	setColor(colors[repeatCount])
	goTo(-width / 2, -width * getSquareRoot(3) / 6)
	beginShape()
	repeat (3) {
		koch_snowflake_side(width, depth)
		turnLeft(120)
	}
	endShape()
	snapshot()
}
greetHolidays()
// Create 3 extra layers so the animation delays at this point
repeat (3) {
	snapshot()
}
// Delete the first layer (current layer) so the animation doesn't start from the ending
pop(0)
// Use layers to create animation
animateLayers()