Search code examples
pythonsvglinebeziercurve

Bézier Curves into straight SVG lines. How to convert?


I am struggling to convert an SVG curve, C into a set of straight lines in Python

Syntax is C x1 y1, x2 y2, x y

Here is an example of what line should I get instead of the curve https://svg-path-visualizer.netlify.app/#M%2010%2010%20C%2020%2020%2C%2040%2020%2C%2050%2010%20M%2010%2010%20L%2020%2017%20L%2040%2017%20L%2050%2010

How to approximate the svg curve with a given set of lines?


Solution

  • get_cubic_bezier_point returns a point on a Cubic Bezier curve, where t is a curve parameter (0 to 1) and p is a list of 4 curve points:

    import numpy as np
    
    def get_cubic_bezier_point(t, p):
      multipliers = [
        t * t * t,
        3 * t * t * (1 - t),
        3 * t * (1 - t) * (1 - t),
        (1 - t) * (1 - t) * (1 - t)
      ]
      x = 0
      y = 0
      for index in range(4):
        x += p[index][0] * multipliers[index]
        y += p[index][1] * multipliers[index]
      return [x,y]
    
    points = [
        [10, 10],
        [100, 250],
        [150, -100],
        [220, 140],
    ]  
    
    for t in np.arange(0, 1, 0.1):
      point = get_cubic_bezier_point(t, points)
      print(point)
    

    Here is a JS snippet to illustrate a CB:

    const points = [
        {x: 10, y: 10},
        {x: 100, y: 250},
        {x: 150, y: -100},
        {x: 220, y: 140}
    ];
    
    const fp = i => `${points[i].x},${points[i].y}`;
    
    d3.select('path').attr('d', `M ${fp(0)} C ${fp(1)} ${fp(2)} ${fp(3)}`);
    
    const findCBPoint = t => {
        const multipliers = [
        t * t * t,
        3 * t * t * (1 - t),
        3 * t * (1 - t) * (1 - t),
        (1 - t) * (1 - t) * (1 - t)
      ]
      return multipliers.reduce((s, m, i) => ({
        x: s.x + points[i].x * m, 
        y: s.y + points[i].y * m
        }), {x: 0, y: 0});
    }
    
    for (let t = 0; t <= 1; t += 0.1) {
      const p = findCBPoint(t);
      console.log(p);
      d3.select('svg').append('circle')
        .attr('cx', p.x).attr('cy', p.y).attr('r', 3);
    }
    path {
      stroke: red;
      fill: none;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
    <svg>
      <path/>
    </svg>