I am looking for a solution to convert a freehand, user drawn SVG path, consisting of lots auf LineTo segments, into a smoother one.
Preferred language would be JavaScri
Let's imagine the user drawing is an array of tuples, we could do something like
const points = [[100, 50], [50, 15], [5, 60], [10, 20], [20, 10], [30, 190], [40, 10], [50, 60], [60, 120], [70, 10], [80, 50], [90, 50], [120, 10], [150, 80], [160, 10] ]
const lineProperties = (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)
}
}
const controlPointCalc = (current, previous, next, reverse) => {
const c = current
const p = previous ? previous : c
const n = next ? next : c
const smoothing = 0.2
const o = lineProperties(p, n)
const rev = reverse ? Math.PI : 0
const x = c[0] + Math.cos(o.angle + rev) * o.length * smoothing
const y = c[1] + Math.sin(o.angle + rev) * o.length * smoothing
return [x, y]
}
const svgPathRender = points => {
const d = points.reduce((acc, e, i, a) => {
if (i > 0) {
const cs = controlPointCalc(a[i - 1], a[i - 2], e)
const ce = controlPointCalc(e, a[i - 1], a[i + 1], true)
return `${acc} C ${cs[0]},${cs[1]} ${ce[0]},${ce[1]} ${e[0]},${e[1]}`
} else {
return `${acc} M ${e[0]},${e[1]}`
}
},'')
return ` `
}
const svg = document.querySelector('.svg')
svg.innerHTML = svgPathRender(points)
Detailed explanations in this article.