# Smooth a Svg path with functional programming

--

## 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.

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> elementconst svgPath = (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)`
• `bezierCommand`: returns a bezier curve command `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`.
• `controlPoint`: returns the coordinates `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`.
• `line`: returns the `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 function is 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) }const line = (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]: coordinatesconst controlPoint = (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) * length  return [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 pointcontrolPointCalc(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 commandconst bezierCommand = 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:

`const smoothing = 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 pointconst 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 commandconst bezierCommandCalc = bezierCommand(controlPointCalc)svg.innerHTML = svgPath(points, bezierCommandCalc)`

Which could be rewritten as a one-liner:

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