Click here to Skip to main content
13,803,275 members
Click here to Skip to main content
Add your own
alternative version

Stats

6.6K views
114 downloads
8 bookmarked
Posted 21 Dec 2016
Licenced CPOL

Functional programming in JavaScript

, 21 Dec 2016
Rate this:
Please Sign up or sign in to vote.
This article discusses functional programming in JavaScript through algebra, numbers, euclidean plane and fractals.

This article is in continuation with articles serie on functional programming paradigm which started with the following one.

Introduction

In JavaScript, functions are nothing but objects. Hence, functions can be constructed, passed as parameter, returned from functions or asigned into variables. Thus, JavaScript have first-class functions. More precisely, JavaScript supports the following:

  • Higher-order functions arguments
  • Higher-order functions results
  • Nested functions
  • Anonymous functions
  • Closures
  • Partial application (ECMAScript 5)

Representing data through functions

Let S be any set of elements abc ... (for instance the books on the table or the points of the Euclidean plane) and let S' be any subset of these elements (for instance the green books on the table or the points in the circle of radius 1 centered at the origin of the Euclidean plane).

The Characteristic Function S'(x) of the set S' is a function which associates either true or false with each element x of S.

S'(x) = true if x is in S'
S'(x) = false if x is not in S'

Let S be the set of books on the table and let S' be the set of green books on the table. Let a and b be two green books, and let c and d be two red books on the table. Then:

S'(a) = S'(b) = true
S'(c) = S'(d) = false

Let S be the set of the points in the Euclidean plane and let S' be the set of the points in the circle of radius 1 centered at the origin of the Euclidean plane (0, 0) (unit circle). Let a and b be two points in the unit circle, and let c and d be two points in a circle of radius 2 centered at the origin of the Euclidean plane. Then:

S'(a) = S'(b) = true
S'(c) = S'(d) = false

Thus, any set S' can always be represented by its Characteristic Function. A function that takes as argument an element and returns true if this element is in S'false otherwise. In other words, a set (abstract data type) can be represented through a function in JavaScript.

function set() { } 

In the next sections, we will see how to represent some fundamental sets in the algebra of sets through JavaScript in a functional way, then we will define generic binary operations on sets. We will then apply these operations on numbers then on subsets of the Euclidean plane. Sets are abstract data structures, the subsets of numbers and the subsets of the Euclidean plane are the representation of abstract data-structures, and finally the binary operations are the generic logics that works on any representation of the abstract data structures.

JavaScript Environment

To run the source code, you'll need a JavaScript engine. There are plenty of JavaScript engines available. In this section, I describe step by step how to set up of the JavaScript environment by using V8 JavaScript engine in Ubuntu 16.04. V8 is an Open Source JavaScript engine written in C++ used in Google Chrome, Node.js and V8.NET. The set up could be done on Windows and Mac as well.

1. Get V8 source code and compile it:

I have successfully installed and executed d8 on Ubuntu 16.04 by running the following commands in a terminal.

$ sudo apt-get install git
$ mkdir ~/js 
$ cd ~/js
$ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git depot_tools
$ export PATH=~/js/depot_tools:"$PATH"
$ fetch v8
$ cd v8
$ make native
$ ~/js/v8/out/native/d8

Downloading V8 source code from the git repository take some time depending on the internet connection speed. Compiling V8 source code take some time too depending on the hardware configuration but after waiting the necessary amount of time everything works just fine.

2. We can write our first JavaScript code:

To load a JavaScript file in the console, you can do it as follows:

load('set.numbers.demo.js')

Sets

This section introduces the representation of some fundamental sets in the algebra of sets through JavaScript.

Empty set

Let E be the empty set and Empty its Characteristic function. In algebra of sets, E is the unique set having no elements. Therefore, Empty can be defined as follows:

Empty(x) = false if x is in E
Empty(x) = false if x is not in E

Thus, the representation of E in JavaScript can be defined as follows:

empty = function() {return function(){return false}}

In algebra of sets, Empty is represented as follows:

Thus, running the code below:

print('\nEmpty set:')
print('Is 7 in {}? ' + empty()(7))

gives the following results:

Set All

Let S be a set and S' be the subset of S that contains all the elements and All its Characteristic function. In algebra of sets,S' is the full set that contains all the elements. Therefore, All can be defined like this:

All(x) = true if x is in S

Thus, the representation of S' in JavaScript can be defined as follows:

all = function() {return function(){return true}}

In algebra of sets, All is represented as follows:

Thus, running the code below:

print('\nSet All:')
print('Is 7 in integers set? ' + all()(7))

gives the following results:

Singleton set

Let E be the Singleton set and Singleton its Characteristic function. In algebra of sets, E also known as unit set, or 1-tuple is a set with exactly one element e. Therefore, Singleton can be defined as follows:

Singleton(x) = true if x is e
Singleton(x) = false if x is not e

Thus, the representation of E in JavaScript can be defined as follows:

singleton = function(x) {return function(y){return x === y}}

Thus, running the code below:

print('\nSingleton set:')
print('Is 7 in the singleton set {0}? ' + singleton(0)(7))
print('Is 7 in the singleton set {7}? ' + singleton(7)(7))

gives the following results:

Other sets

This section presents subsets of the integers set.

Even numbers

Let E be the set of even numbers and Even its Characteristic function. In mathematics, an even number is a number which is a multiple of two. Therefore, Even can be defined as follows:

Even(x) = true if x is a multiple of 2
Even(x) = false if x is not a multiple of 2

Thus, the representation of E in JavaScript can be defined as follows:

even = function(x){return x%2 === 0}

Thus, running the code below:

print('\nEven numbers set:')
print('Is 99 in even numbers set? ' + even(99))
print('Is 998 in even numbers set? ' + even(998))

gives the following results:

Odd numbers

Let E be the set of odd numbers and Odd its Characteristic function. In mathematics, an odd number is a number which is not a multiple of two. Therefore, Odd can be defined as follows:

Odd(x) = true if x is not a multiple of 2
Odd(x) = false if x is a multiple of 2

Thus, the representation of E in JavaScript can be defined as follows:

odd = function(x){return x%2 === 1}

Thus, running the code below:

print('\nOdd numbers set:')
print('Is 99 in odd numbers set? ' + odd(99))
print('Is 998 in odd numbers set? ' + odd(998))

gives the following results:

Multiples of 3

Let E be the set of multiples of 3 and MultipleOfThree its Characteristic function. In mathematics, a multiple of 3 is a number divisible by 3. Therefore, MultipleOfThree can be defined as follows:

MultipleOfThree(x) = true if x is divisible by 3
MultipleOfThree(x) = false if x is not divisible by 3

Thus, the representation of E in JavaScript can be defined as follows:

multipleOfThree = function(x){return x%3 === 0}

Thus, running the code below:

print('\nMultiples of 3 set:')
print('Is 99 in multiples of 3 set? ' + multipleOfThree(99))
print('Is 998 in multiples of 3 set? ' + multipleOfThree(998))

gives the following results:

Multiples of 5

Let E be the set of multiples of 5 and MultipleOfFive its Characteristic function. In mathematics, a multiple of 5 is a number divisible by 5. Therefore, MultipleOfFive can be defined as follows:

MultipleOfFive(x) = true if x is divisible by 5
MultipleOfFive(x) = false if x is not divisible by 5

Thus, the representation of E in JavaScript can be defined as follows:

multipleOfFive = function(x){return x%5 === 0}

Thus, running the code below:

print('\nMultiples of 5 set:')
print('Is 15 in multiples of 5 set? ' + multipleOfFive(15))
print('Is 998 in multiples of 5 set? ' + multipleOfFive(998))

gives the following results:

Prime numbers

A long time ago, When I was playing with Project Euler problems, I had to resolve the following one:

By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that the 6th prime is 13.
What is the 10 001st prime number?

To resolve this problem, I first had to write a fast algorithm that checks whether a given number is prime or not. Once the algorithm written, I wrote an iterative algorithm that iterates through primes until the 10 001st prime number was found.

Let E be the set of primes and Prime its Characteristic function. In mathematics, a prime is a natural number greater than 1 that has no positive divisors other than 1 and itself. Therefore, Prime can be defined as follows:

Prime(x) = true if x is prime
Prime(x) = false if x is not prime

Thus, the representation of E in JavaScript can be defined as follows:

prime = function(x){
 if(x===1) return false
 if(x<4) return true
 if(x%2===0) return false
 if(x<9) return true
 if(x%3===0) return false
 var sqrt = Math.sqrt(x)
 for(var i = 5; i <= sqrt; i+=6){
  if(x%i===0) return false
  if(x%(i+2)===0) return false
 }
 return true
}

Thus, running the code below to resolve our problem:

print('\nPrimes set:')
print('Is 2 in primes set? ' + prime(2))
print('Is 4 in primes set? ' + prime(4))
print('The 10 001st prime number is ' + getPrime(10001))

where getPrime is defined below:

getPrime = function(p){
 var count = 0;
 for(var i = 1; ; i++){
   if(prime(i)) count++
   if(count === p){
     return i
     break
   }
 }
}

gives the following results:

Binary operations

This section presents several fundamental operations for constructing new sets from given sets and for manipulating sets. Below the Ven diagram in the algebra of sets.

Union

Let E and F be two sets. The union of E and F, denoted by E U F is the set of all elements wich are members of either E and F.

Let Union be the union operation. Thus, the Union operation can be implemented as follows in JavaScript:

union = function(e, f){return function(x){ return e(x) || f(x)}}

Running the code below:

print('\nUnion:')
print('Is 7 in the union of Even and Odd Integers Set? ' + union(even,odd)(7))

gives the following results:

Intersection

Let E and F be two sets. The intersection of E and F, denoted by E n F is the set of all elements wich are members of both E and F.

Let Intersection be the intersection operation. Thus, the Intersection operation can be implemented as follows in JavaScript:

intersection = function(e, f){return function(x){ return e(x) && f(x)}}

Running the code below:

print('\nIntersection:')
multiplesOfThreeAndFive = intersection(multipleOfThree, multipleOfFive)
print('Is 15 a multiple of 3 and 5? ' + multiplesOfThreeAndFive(15))
print('Is 10 a multiple of 3 and 5? ' + multiplesOfThreeAndFive(10))

gives the following results:

Cartesian product

Let E and F be two sets. The cartesian product of E and F, denoted by E × F is the set of all ordered pairs (e, f) such that e is a member of E and f is a member of F.

Let CartesianProduct be the cartesian product operation. Thus, the CartesianProduct operation can be implemented as follows in JavaScript:

cartesianProduct = function(e, f){return function(x, y){ return e(x) && f(y)}}

Running the code below:

print('\nCartesian Product:')
cp = cartesianProduct(multipleOfThree,multipleOfFive)
print('Is (9, 15) in MultipleOfThree x MultipleOfFive? ' + cp(9, 15))

gives the following results:

Complements

Let E and F be two sets. The relative complement of F in E, denoted by E \ F is the set of all elements wich are members of E but not members of F.

Let Complement be the relative complement operation. Thus, the Complement operation can be implemented as follows in JavaScript:

complement = function(e, f){return function(x){ return e(x) && !f(x)}}

Running the code below:

print('\nComplement:')
c = complement(multipleOfThree, multipleOfFive)
print('Is 15 in MultipleOfThree \\ MultipleOfFive set? '+ c(15))
print('Is 9 in MultipleOfThree \\ MultipleOfFive set? '+ c(9))

gives the following results:

Symmetric Difference

Let E and F be two sets. The symmetric difference of E and F, denoted by E delta F is the set of all elements wich are members of either E and F but not in the intersection of E and F.

Let SymmetricDifference be the symmetric difference operation. Thus, the SymmetricDifference operation can be implemented in two ways in JavaScript. A trivial way is to use the union and complement operations as follows:

symmetricDifferenceWithoutXor = function(e, f){ return function(x){ return union(complement(e,f), complement(f, e))(x)}}

Another way is to use the XOR binary operation as follows:

symmetricDifferenceWithXor = function(e, f){return function(x){ return (e(x) ^ f(x)) === 1 ? true : false}}

Running the code below:

print('\nSymmetricDifference without XOR:')
sdWithoutXor = symmetricDifferenceWithoutXor(prime, even)
print('Is 2 in the symetric difference of prime and even Sets? ' + sdWithoutXor(2))
print('Is 4 in the symetric difference of prime and even Sets? ' + sdWithoutXor(4))
print('Is 7 in the symetric difference of prime and even Sets? ' + sdWithoutXor(7))

print('\nSymmetricDifference with XOR:')
sdWithXor = symmetricDifferenceWithXor(prime, even)
print('Is 2 in the symetric difference of prime and even Sets? ', sdWithXor(2))
print('Is 4 in the symetric difference of prime and even Sets? ', sdWithXor(4))
print('Is 7 in the symetric difference of prime and even Sets? ', sdWithXor(7))

gives the following results:

Other operations

This section presents other useful binary operations on sets.

Contains

Let Contains be the operation that checks whether or not an element is in a set. This operation is a function that takes as parameter an element and returns true if the element is in the set, false otherwise.

Thus, this operation is defined as follows in JavaScript:

contains = function(e, x){return e(x)}

Therefore, running the code below:

print('\nContains:')
print('Is 7 in the singleton {0}? ' + contains(singleton(0), 7))
print('Is 7 in the singleton {7}? ' + contains(singleton(7), 7))

gives the following result:

Add

Let Add be the operation that adds an element to a set. This operation is a function that takes as parameter an element and adds it to the set.

Thus, this operation is defined as follows in JavaScript:

add = function(e, y){return function(x){ return x === y || e(x)}}

Therefore, running the code below:

print('\nAdd:')
print('Is 7 in {0, 7}? ' + add(singleton(0),7)(7))
print('Is 0 in {1, 0}? ' + add(singleton(1),0)(0))
print('Is 7 in {19, 0}? ' + add(singleton(19),0)(7))

gives the following result:

Remove

Let Remove be the operation that removes an element from a set. This operations is a function that takes as parameter an element and removes it from the set.

Thus, this operation is defined as follows in JavaScript:  

remove = function(e, y){return function(x){ return x !== y && e(x)}}

Therefore, running the code below:

print('\nRemove:')
print('Is 7 in {}? ' + remove(singleton(0), 0)(7))
print('Is 0 in {}? ' + remove(singleton(7), 7)(0))

gives the following result:

For those who want to go further

You can see how easy we can do some algebra of sets in JavaScript through functional programming. In the previous sections was shown the most fundamental definitions. But, If you want to go further, you can think about:

  • Relations over sets
  • Abstract algebra, such as monoids, groups, fields, rings, K-vectorial spaces and so on
  • Inclusion-exclusion principle
  • Russell's paradox
  • Cantor's paradox
  • Dual vector space
  • Theorems and Corollaries

Euclidean Plane

In the previous section, the fundamental concepts on sets were implemented in JavaScript. In this section we will practice the concepts implemented on the set of plane points (Euclidean plane).

Drawing a disk

A disk is a subset of a plane bounded by a circle. There are two types of disks. Closed disks which are disks that contain the points of the circle that constitutes its boundary, and Open disks wich are disks that do not contain the points of the circle that constitutes its boundary.

In this section, we will set up the Characterstic function of the Closed disk and draw it in HTML5.

To set up the Characterstic function we need first a function that calculates the Euclidean Distance between two points in the plane. This function is implemented as follows:

function distance(p1, p2){
 return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2))
}

where point is defined below:

point = function(x,y){
 this.x = x
 this.y = y
}

This formula is based on Pythagoras' Theorem.

where c is the Euclidean distance is (p1.X - p2.X)² and  is (p1.Y - p2.Y)².

Let Disk be the Characteristic function of a closed disk. In algebra of sets, the definition of a closed disk in the reals set is as follows:

where a and b are the coordinates of the center and R the radius.

Thus, the implementation of Disk in JavaScript is as follows:

disk = function(center, radius){return function(p){ return distance(p, center) <= radius}}

In order to view the set in a façade, I decided to implement a function draw that draws a set in the Euclidean plane. I chose HTML5 and thus used the canvas element for drawing.

Thus, I've built the Euclidean plane illustrated below through the method draw.

Below the implementation of the plane.

/*
 * Plane
 *
*/
plane = function(width, height) {

 /**
  * Plane width in pixels
  *
 */
 this.width = width

 /**
  * Plane height in pixels
  *
 */
 this.height = height

 /*
  * Draws a generic set
  *
 */
 this.draw = function (set, canvasId){
  var canvas = document.getElementById(canvasId)
  canvas.width = this.width
  canvas.height = this.height
  var context = canvas.getContext('2d'), 
  semiWidth = this.width/2, semiHeight = this.height/2, 
  xMin = -semiWidth, xMax = semiWidth,
  yMin = -semiHeight, yMax = semiHeight
  for(var x = 0; x < this.width; x++){
   var xp = xMin + x * (xMax - xMin) / this.width
   for(var y = 0; y < this.height; y++) {
     var yp = yMax - y * (yMax - yMin) / this.height
     if(set(new point(xp, yp))) context.fillRect(x, y, 1, 1)
    }
   }
  }
}

In the draw method, a canvas having the same width and same height as the Euclidean plane container is created. Then each point in pixels (x,y) of the canvas is replaced by a black point if it belongs to the setxMinxMaxyMin and yMax are the bounding values illustrated in the figure of the Euclidean plane above.

Running the code below:

euclideanPlane = new plane(200, 200)
euclideanPlane.draw(disk(new point(0, 0), 50), 'disk')

where disk is the id of the canvas:

<canvas id="disk">Your browser does not support HTML5 canvas.</canvas>

gives the following façade:

Drawing horizontal and vertical half-planes

horizontal or a vertical half-plane is either of the two subsets into which a plane divides the Euclidean space. A horizontalhalf-plane is either of the two subsets into which a plane divides the Euclidean space through a line perpendicular with the Y axis like in the figure above. A vertical half-plane is either of the two subsets into which a plane divides the Euclidean space through a line perpendicular with the X axis.

In this section, we will set up the Characteristic functions of the horizontal and vertical half-planes, draw them in HTML5 and see what we can do if we combine them with the disk subset.

Let HorizontalHalfPlane be the Characteristic function of a horizontal half-plane. The implementation of HorizontalHalfPlane in JavaScript is as follows:

horizontalHalfPlane = function(y, isLowerThan){return function(p) { return isLowerThan ? p.y <= y : p.y >= y}}

Thus, running the code below:

euclideanPlane.draw(horizontalHalfPlane(0, true), 'hhp')

where hhp is the id of the canvas:

<canvas id="hhp">Your browser does not support HTML5 canvas.</canvas>

gives the following façade:

Let VerticalHalfPlane be the Characteristic function of a vertical half-plane. The implementation of VerticalHalfPlane in JavaScript is as follows:

verticalHalfPlane = function(x, isLowerThan){return function(p) { return isLowerThan ? p.x <= x : p.x >= x}}

Thus, running the code below:

euclideanPlane.draw(verticalHalfPlane(0, false), 'vhp')

where vhp is the id of the canvas:

<canvas id="vhd">Your browser does not support HTML5 canvas.</canvas>

gives the following façade:

In the first section of the article we set up basic binary operations on sets. Thus, by combining the intersection of a disk and a half-plane for example, we can draw the half-disk subset.

Therefore, running the sample below:

euclideanPlane.draw(intersection(disk(new point(0, 0), 50), verticalHalfPlane(0, false)), 'hd')

where hd is the id of the canvas:

<canvas id="hd">Your browser does not support HTML5 canvas.</canvas>

gives the following façade:

Functions

This section presents functions on the sets in the Euclidean plane.

Translate

Let translatePoint be the function that translates a point in the plane. In Euclidean geometry, translatePoint is a function that moves a given point a constant distance in a specified direction. Thus the implementation in JavaScript is as follows:

translatePoint = function(deltax, deltay){return function(p){ return new point(p.x + deltax, p.y + deltay)}}

where (deltax, deltay) is the constant vector of the translation.

Let translate be the function that translates a set in the plane. This function is simply implemented as follows in JavaScript:

translate = function(e, deltax, deltay){return function(p){ return e(translatePoint(-deltax, -deltay)(p))}}

translate takes as parameters deltax which is the delta distance in the first Euclidean dimension and deltay which is the delta distance in the second Euclidean dimension. If a point P (x, y) is translated in a set S, then its coordinates will change to (x', y') = (x + delatx, y + deltay). Thus, the point (x' - delatx, y' - deltay) will always belong to the set S. In set algebra, translate is called isomorph, in other words the set of all translations forms the translation group T, which is isomorphic to the space itself. This explains the main logic of the function.

Thus, running the code below :

var deltay = 0
setInterval(function(){
 deltay = deltay <=  euclideanPlane.height ? deltay + 20 : 0
 euclideanPlane.draw(translate(disk(new point(0, -50), 50), 0, deltay) , 'ep_op')
}, 1000)

where ep_op is the id of the canvas:

<canvas id="ep_op">Your browser does not support HTML5 canvas.</canvas>

gives the following façade:

Homothety

Let scalePoint be the function that sends any point M to another point N such that the segment SN is on the same line as SM, but scaled by a factor lambda. In algebra of sets, Scale is formulated as follows:

Thus the implementation in JavaScript is as follows:

scalePoint = function(lambdax, lambday, deltax, deltay){return function(p){ return new point(lambdax * p.x + deltax, lambday * p.y + deltay)}}

where (deltax, deltay) is the constant vector of the translation and (lambdax, lambday) is the lambda vector.

Let scale be the function that applies an homothety on a set in the plan. This function is simply implemented as follows in JavaScript:

scale = function(e, lambdax, lambday, deltax, deltay){return function(p){ return e(scalePoint(1/lambdax, 1/lambday, -deltax/lambdax, -deltay/lambday)(p))}}

scale takes as parameters deltax which is the delta distance in the first Euclidean dimension, deltay which is the delta distance in the second Euclidean dimension and (lambdax, lambday) wich is the constant factor vector lambda. If a point P (x, y) is transformed through scale in a set S, its coordinates will change to (x', y') = (lambdax * x + delatx, lambday * y + deltay). Thus, the point ((x'- delatx)/lambdax, (y' - deltay)/lambday) will always belong to the set S, If lambda is different from the vector 0, of course. In algebra of sets, scale is called isomorph, in other words the set of all homotheties forms the Homothety group H, wich is isomorphic to the space itself \ {0}. This explains the main logic of the function.

Thus, running the code below :

var deltay = 0, lambday = 0.05 
setInterval(function(){  
 deltay = deltay <=  euclideanPlane.height ? deltay + 20 : 0
 lambday = deltay <=  euclideanPlane.height ? lambday + 0.05 : 0.05
 euclideanPlane.draw(scale(disk(new point(0, -50), 50), 1, lambday, 0, deltay), 'ep_op')
}, 1000)

gives the following façade:

Rotate

Let rotatePoint be the function that rotates a point with an angle theta. In matrix algebra, rotatePoint is formulated as follows:

where (x', y') are the co-ordinates of the point after rotation, and the formula for x' and y' is as follows:

The demonstration of this formula is very simple. Have a look at this rotation.

Below the demonstration:

Thus the implementation in JavaScript is as follows:

rotatePoint = function(theta){ return function(p){ return new point(p.x*Math.cos(theta)-p.y*Math.sin(theta), p.x*Math.sin(theta)+p.y*Math.cos(theta))}}

Let rotate be the function that applies a rotation on a set in the plane with the angle theta. This function is simply implemented as follow in JavaScript.

rotate = function(e, theta){ return function(p){return e(rotatePoint(-theta)(p))}}

rotate is a function that takes as parameter theta which is the angle of the rotation. If a point P (x, y) is transformed through rotate in a set S, then its coordinates will change to (x', y') = (x * cos(theta) - y * sin(theta), x * sin(theta) + y * cos(theta)). Thus, the point (x' * cos(theta) + y' * sin(theta), y' * cos(theta) - x' * sin(theta)) will always belong to the set S. In algebra of sets, rotate is called isomorph, in other words the set of all rotations forms the Rotation group R, which is isomorphic to the space itself. This explains the main logic of the function.

Thus, running the code below :

var theta = 0
setInterval(function(){
 euclideanPlane.draw(rotate(horizontalHalfPlane(-90, true), theta), 'ep_op')
 theta = (theta + Math.PI/2)%(2*Math.PI)
}, 1000)

gives the following façade:

For those who want to go further

Very simple, isn't it? For those who want to go further, you can explore these:

  • Ellipse
  • Three-dimensional Euclidean space
  • Ellipsoide
  • Paraboloid
  • Hyperboloid
  • Spherical harmonics
  • Superellipsoid
  • Haumea
  • Homoeoid
  • Focaloid

Fractals

Fractals are sets that have a fractal dimension that usually exceeds their topological dimension and may fall between the integers. For example, the Mandelbrot set is a fractal defined by a family of complex quadratic polynomials:

Pc(z) = z^2 + c

where c is a complex. The Mandelbrot fractal is defined as the set of all points c such that the above sequence does not escape to infinity. In algebra of sets, this is formulated as follows:

Fractals (abstract data type) can always be represented as follows in JavaScript:

function fractal() {}

Complex numbers and drawing

In order to be able to draw fractals, I needed to manipulate Complex numbers. Thus, I've created the complex class below:

complex = function(r, i){
 this.x = r
 this.y = i
}

complex.prototype.toString = function(){
 return this.x + ' + i * ' + this.y
}

complex.prototype.abs = function(){
 return Math.sqrt(this.x*this.x+this.y*this.y)
}


complex.add = function(z1, z2){
 return new complex(z1.x+z2.x, z1.y+z2.y)
}

complex.substract = function(z1, z2){
 return new complex(z1.x-z2.x, z1.y-z2.y)
}

complex.multiply = function(z1, z2){
 return new complex(z1.x*z2.x - z1.y * z2.y, z1.x * z2.y+  z1.y * z2.x)
}

complex.zero = new complex(0, 0)

Mandelbrot Fractal

I've created a Mandelbrot Fractal (abstract data type representation) P(z) = z^2 + c that is available below.

mandelbrot = function(c, z){return complex.add(complex.multiply(z, z), c)}

In order to be able to draw Complex numbers, I created a complexPlane class. Below the implementation in JavaScript.

/*
 * Plane
 *
*/
complexPlane = function(width, height, real_min, real_max, imaginary_min, imaginary_max, boundary, fractalIterationsPerPixel, canvasId) {

 /**
  * Plane width in pixels
  *
 */
 this.width = width

 /**
  * Plane height in pixels
  *
 */
 this.height = height

 /**
  * Real axis minimum value
  *
 */
 this.real_min = real_min

 /**
  * Real axis maximum value
  *
 */
 this.real_max = real_max

 /**
  * Imaginary axis minimum value
  *
 */
 this.imaginary_min = imaginary_min

 /**
  * Imaginary axis maximum value
  *
 */
 this.imaginary_max = imaginary_max

 /**
  * Boudary
  *
 */
 this.boundary = boundary

 /**
  * Number of Zn iterations per pixel
  *
 */
 this.fractalIterationsPerPixel = fractalIterationsPerPixel
 
 /**
  * Canvas Identifier
  *
 */
 this.canvasId = canvasId

 /*
  * Draws a fractal
  *
 */
 this.draw = function (fractal){
  var canvas = document.getElementById(this.canvasId)
  canvas.width = this.width
  canvas.height = this.height
  var context = canvas.getContext('2d')
  context.fillStyle = 'yellow'
  for(var x = 0; x < this.width; x++){
   var xp = this.real_min + x * (this.real_max - this.real_min) / this.width
   for(var y = 0; y < this.height; y++) {
     var yp = this.imaginary_max - y * (this.imaginary_max - this.imaginary_min) / this.height
     var c = new complex(xp, yp)
     var z = complex.zero
     for(var k=0; k< this.fractalIterationsPerPixel;k++) z = fractal(c, z)     
     if(z.abs() < this.boundary) context.fillRect(x, y, 1, 1)   
    }
   }
  }
 
 /*
  * Displays 'Please wait...' at the center of the canvas
  *
 */ 
 this.pleaseWait = function(){
  var canvas = document.getElementById(this.canvasId)
  canvas.width = this.width
  canvas.height = this.height
  var context = canvas.getContext('2d')
  context.fillStyle = 'yellow'
  context.fillText('Please wait...', this.width/2 - 30, this.height/2)
 }
}

Thus, running the code below:

complexPlane = new complexPlane(300, 300, -1.5, 1.5, -1.5, 1.5, 1.5, 20, 'fractal')

mandelbrot = function(c, z){return complex.add(complex.multiply(z, z), c)}

complexPlane.pleaseWait()

setTimeout(function(){ complexPlane.draw(mandelbrot) }, 500)

where fractal is the id of the canvas:

<canvas id="fractal">Your browser does not support HTML5 canvas.</canvas>

gives the following façade:

For those who want to go further

For those who want to go further, you can explore these:

  • Newton Fractals
  • Julia Fractals
  • Other Fractals

That's it! I hope you enjoyed reading.

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Akram El Assas
Engineer
Morocco Morocco
Akram El Assas graduated from the french engineering school ENSEIRB located in Bordeaux, a city in the south of France, and got his diploma in software engineering in 2010. He worked for about 3 years in France for Mediatvcom, a company specialized in audiovisual, digital television and new technologies. Mediatvcom offers services such as consulting, project management, audit and turnkey solutions adapted to the needs of customers. Akram worked mainly with Microsoft technologies such as C#, ASP.NET and SQL Server but also with JavaScript, jQuery, HTML5 and CSS3. Akram worked on different projects around digital medias such as Media Asset Management systems, Digital Asset Management systems and sometimes on HbbTV apps.

Akram went back to his native country Morocco in 2013. Since then, he started discovering Android and Java development but also continued working with Microsoft technologies.

You may also be interested in...

Comments and Discussions

 
GeneralMy vote of 5 Pin
Afzaal Ahmad Zeeshan21-Dec-16 6:40
mvpAfzaal Ahmad Zeeshan21-Dec-16 6:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web03 | 2.8.181215.1 | Last Updated 21 Dec 2016
Article Copyright 2016 by Akram El Assas
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid