# Smooth a Svg path with functional programming

## How to use **pure functions**, **closures** and **functions composition**

--

## In a previous article we went through the steps to **Smooth a svg path with cubic bezier curves**. Here, we are going to refactor this code with functional programming.

Functional programming is the process of building software by composing pure functions, avoiding shared state, mutable data, and side-effects.

**Imperative code has a few problems:**

- Functions can rely on external states (variables or functions referenced from a higher scope) which can have side effects.
- There is no explicit relations between functions.

With a large codebase, this is difficult to maintain and test. **Functional programming** aims to solving these problems.

**So let’s refactor the imperative code from ****the previous article**** **and use concepts related to functional programming: **pure functions**, **closures** and **functions composition**.

# What are we trying to achieve?

Given an array of tuples representing the coordinates of a line:

`const points = [[5, 10], [10, 40], [40, 30], [60, 5], [90, 45], [120, 10], [150, 45], [200, 10]]`

We want to create a `<path>`

element whose `d`

attribute defines a smooth line with one *moveto* command `M x,y`

followed by several *bezier curve* commands `C x1,y1, x2,y2, x,y`

. The result is like:

`<path d="M 5,10 C 6,16 3,36 10,40 C 17,44 30,37 40,30 C 50,23 50,2 60,5 C 70,8 78,44 90,45 C 102,46 108,10 120,10 C 132,10 134,45 150,45 C 166,45 190,17 200,10" fill="none" stroke="grey"></path>`

And renders like that:

For a complete explanation about the trigonometry calculations, please check the previous article.

## What we already have

From this article, we have a `svgPath`

function to loop over the `points`

array and return a `<path>`

:

//Render the svg <path> element

//I: - points (array): points coordinates

// - command (function)

//I: - point (array) [x,y]: current point coordinates

// - i (integer): index of 'point' in the array 'a'

// - a (array): complete array of points coordinates

//O: - (string) a svg path command

//O: - (string): a Svg <path> elementconstsvgPath= (points, command)=> { // build the d attributes by looping over the points

const d =points.reduce((acc, point, i, a) => i === 0 // if first point

? `M ${point[0]},${point[1]}` // else

: `${acc} ${command(point, i, a)}`

, '')return `<path d="${d}" fill="none" stroke="grey" />`

}

Eventually `svgPath`

is used like so:

`svg.innerHTML = `**svgPath**(points, bezierCommand)

: returns a bezier curve command**bezierCommand**`C x1,y1 x2,y2 x,y`

from the property of the current`point`

and the`points`

array. It depends on a`controlPoint`

function to find`x1,y1`

and`x2,y2`

.

: returns the coordinates**controlPoint**`x, y`

of a control point from the`current`

,`previous`

and`next`

points. It depends on a`line`

function to find the property of the line joining`previous`

and`next`

.

: returns the**line**`angle`

and the`length`

of a line from the coordinates of two points. It does not depend on any external variables.

# Pure functions

A

pure functionis a function which:- Given the same inputs, always returns the same output.

- Has no side-effects.

The `line`

function already respects this definition, so we reuse it as is:

//Properties of a line

//I: - pointA (array) [x,y]: coordinates

// - pointB (array) [x,y]: coordinates

//O: - (object) { length: (integer), angle: (integer) }constline= (pointA,pointB) => {

const lengthX = pointB[0] - pointA[0]

const lengthY = pointB[1] - pointA[1]return {

length: Math.sqrt(Math.pow(lengthX, 2) + Math.pow(lengthY, 2)),

angle: Math.atan2(lengthY, lengthX)

}

}

The two other functions `controlPoint`

and `bezierCommand`

are not *pure *because they reference variables from the global scope:

`controlPoint`

references the`line`

function and a`smooth`

variable.`bezierCommand`

references the`controlPoint`

function.

**How to transform these in pure functions?**

**A first idea would be to pass the external variable as an additional parameter. **For example, pass a `line`

parameter to `controlPoint`

like so: `controlPoint(current, previous, next, reverse, line)`

. The downsides of this solution are:

- The need to pass
`line`

to`controlPoint`

each time we want to use it, which is not necessary because`line`

is the same for every points. - If
`controlPoint`

is nested inside other functions,`line`

has to be passed through all the parent functions chain. In this case we have to modify each parent function definition, and thus making them less generic.

**A better idea is to use closures.**

# Closures

A closure is a persistent local variable scope.

**The general mechanism is to return an existing function from a new function which takes variables that used to be external as parameters. **Then, the former function becomes a closure and holds on these variables.

`newFunction = usedToBeExternal => oldFunction`

To apply that to our `controlPoint`

function, we create a new function that takes two parameters, `lineCalc`

and `smooth`

, then returns our actual function as a closure which holds on these variables:

// Create a function to calculate the position of the control point

//I: - lineCalc (function)

//I: - pointA (array) [x, y]: coordinates

// - pointB (array) [x, y]: coordinates

//O: - (object) { length: (integer), angle: (integer) }

// - smooth (float)

//O: - (function) closure

//I: - current (array) [x, y]: coordinates

// - previous (array) [x, y]: coordinates

// - next (array) [x, y]: coordinates

// - reverse (boolean, optional): sets the direction

//O: - (array) [x,y]: coordinatesconstcontrolPoint= (lineCalc,smooth) => (current,previous,next,reverse) => {

// when 'current' is the first or last point of the array

// 'previous' and 'next' are undefined

// replace with 'current'

const p =previous||current

const n =next||current// properties of the line between previous and next

const l =lineCalc(p, n) // If is end-control-point, add PI to the angle to go backward

const angle = l.angle + (reverse? Math.PI : 0)

const length = l.length *smooth// The control point position is relative to the current point

const x =current[0] + Math.cos(angle) * length

const y =current[1] + Math.sin(angle) * lengthreturn [x, y]

}

This is used like so:

// Create a closure function

const controlPointCalc =controlPoint(line, smoothing)// For each point of the array, find the position of a control point

controlPointCalc(current, previous, next, reverse)

Same technic with the `bezierCommand`

function used to calculate the svg cubic bezier command on each point. This function takes `controlPoint`

as a parameter and returns a closure function:

//Create a function to calculate a bezier curve command

//I: - controlPointCalc (function)

//I: - current (array) [x, y]: current point coordinates

// - previous (array) [x, y]: previous point coordinates

// - next (array) [x, y]: next point coordinates

// - reverse (boolean) to set the direction

//O: - (array) [x, y]: coordinates of a control point

//O: - (function) closure

//I: - point (array) [x,y]: current point coordinates

// - i (integer): index of 'point' in the array 'a'

// - a (array): complete array of points coordinates

//O: - (string) 'C x2,y2 x1,y1 x,y': cubic bezier commandconstbezierCommand=controlPointCalc=> (point,i,a) => { // start control point

const [cpsX, cpsY] =controlPointCalc(a[i-1], a[i-2], point) // end control point

const [cpeX, cpeY] =controlPointCalc(point, a[i-1], a[i+1], true)return `C ${cpsX},${cpsY} ${cpeX},${cpeY} ${point[0]},${point[1]}`

}

# Functions composition

Functions composition is

a mechanism to combine simple functions to build more complicated ones.

Now all our *pure functions* are ready, we can finally compose them:

constsmoothing= 0.2//Position of a control point

//I: - current (array) [x, y]: coordinates

// - previous (array) [x, y]: coordinates

// - next (array) [x, y]: coordinates

// - reverse (boolean, optional): sets the direction

//O: - (array) [x, y]: coordinates of a control point

const controlPointCalc =controlPoint(line,smoothing)//Bezier curve command

//I: - point (array) [x,y]: current point coordinates

// - i (integer): index of 'point' in the array 'a'

// - a (array): complete array of points coordinates

//O: - (string) 'C x2,y2 x1,y1 x,y': cubic bezier command

const bezierCommandCalc =bezierCommand(controlPointCalc)svg.innerHTML =svgPath(points, bezierCommandCalc)

Which could be rewritten as a one-liner:

`svg.innerHTML = `**svgPath**(points, **bezierCommand**(**controlPoint**(**line**, **smoothing**)))