javascriptsvgcanvasbezier

SVG 'S' Bezier curve to mathematical formulae


I have the following SVG path: M 0, 100 S 27, 2, 200 0.

I'm trying to use the Bezier equations from here to convert the path into mathematical formulae.

When I try, I notice that the blue line (using the SVG path string) doesn't quite match the red line (which is drawn using the mathematical formulae).

Am I doing something wrong, such as not properly understanding how to extract control points from the SVG path?

let c = document.getElementById('c')
let ctx = c.getContext('2d')

ctx.strokeStyle = 'blue'
ctx.stroke(new Path2D(`M 0, 100  S 27, 2, 200 0`))

// bezier control points, derived from SVG path
let x1 = 0,    y1 = 100,
    x2 = 27,   y2 = 2,
    x3 = 200,  y3 = 0

ctx.strokeStyle = 'red'
ctx.beginPath()
ctx.moveTo(x1, y1)

for(let t=0; t<=1; t+=0.0001) {
  ctx.lineTo(
    (1-t)**2 * x1 + 2*(1-t) * t * x2 + t**2 * x3,
    (1-t)**2 * y1 + 2*(1-t) * t * y2 + t**2 * y3
  )
}
ctx.stroke()
<canvas id="c" width="200" height="100"></canvas>


Solution

  • The 'S' path command will draw a cubic Bézier curve, but your code seems to be drawing a quadratic curve. If I read the SVG specs right (that’s a big "if", it’s not yet 7 in the morning here...), I think you need to repeat the (0, 100) point to get four points, and then generate a cubic curve from those.

    Working code:

    let c = document.getElementById('c')
    let ctx = c.getContext('2d')
    
    ctx.strokeStyle = 'blue'
    ctx.stroke(new Path2D(`M 0, 100  S 27, 2, 200 0`))
    
    // bezier control points, derived from SVG path
    let x1 = 0,    y1 = 100,
        x2 = 0,    y2 = 100,
        x3 = 27,   y3 = 2,
        x4 = 200,  y4 = 0
    
    ctx.strokeStyle = 'red'
    ctx.beginPath()
    ctx.moveTo(x1, y1)
    
    for(let t=0; t<=1; t+=0.0001) {
      ctx.lineTo(
        (1-t)**3 * x1 + 3*(1-t)**2 * t * x2 + 3*(1-t) * t**2 * x3 + t**3 * x4,
        (1-t)**3 * y1 + 3*(1-t)**2 * t * y2 + 3*(1-t) * t**2 * y3 + t**3 * y4,
      )
    }
    ctx.stroke()
    <canvas id="c" width="200" height="100"></canvas>