Search code examples
javascriptsvgbezier

How to convert SVG 'c' curve to a list of cubic bezier curve?


I have a path as follow:

d="m 0,0 38.913,455.481 c 7.122,83.37 6.816,164.779 13.005,236.077 18.924,218.078 232.099,308.663 268.399,493.918 16.874,86.119 -37.253,229.874 -42.144,323.022 -7.527,143.381 69.579,142.669 104.526,244.648"

I don't quite understand how it is represented.

I guess it is converted as a list of multiple simple cubic bezier curves, but how? What should i take as control points, start point, end point each time?

And what coordinates here exactly are relatives? And relative to what?

I am totally new to SVG graphics and (it seems to me that) w3 references doesn't give enough details on those points.

If it can help my goal here, is to represent this curve with javascript in a canvas point by point.


Solution

  • 1. Convert via yqnn's svg path editor

    An easy way to rewrite your pathdata d attribute is to insert the string in yqnn's svg path editor and
    toggle the minify option checkbox twice.

    All omitted command letters (for repeated and implicit commands) will be added for a more readable d string.

    2. Convert via getPathData()

    getPathData() is not yet supported by any major browser.
    So you need a polyfill like Jarek Foksa's pathdata polyfill.

    1. Parse the path with getPathData().
      All repeated or implicit commands will be split to separate commands:
      Implicit relative l lineto:
    m 0,0 38.913,455.481 
    // result:
    M 0,0 l 38.913,455.481   
    

    The first m command in a d attribute is in fact always absolute. Since we have converted the implicit l command – we can change the first moveto to uppercase M command letter (can facilitate path concatenation).

    Repeated c (cubic polybézier)

    c 7.122 83.37 6.816 164.779 13.005 236.077   
    18.924 218.078 232.099 308.663 268.399 493.918 
    16.874 86.119 -37.253 229.874 -42.144 323.022 
    -7.527 143.381 69.579 142.669 104.526 244.648  
    // result:
    c 7.122 83.37 6.816 164.779 13.005 236.077   
    c 18.924 218.078 232.099 308.663 268.399 493.918 
    c 16.874 86.119 -37.253 229.874 -42.144 323.022 
    c -7.527 143.381 69.579 142.669 104.526 244.648
    
    1. Apply the pathData with setPathData()

    let pathData = path.getPathData();
    path.setPathData(pathData);
    output.value = path.getAttribute('d');
    svg{
    height:20em
    }
    
    textarea{
    width:100%;
    min-height:20em;
    }
    <script src="https://cdn.jsdelivr.net/npm/path-data-polyfill@1.0.4/path-data-polyfill.min.js"></script>
    
    
    <svg viewBox="0 0 382.7 1753.15">
            <path id="path" d="m 0,0 38.913,455.481 c 7.122,83.37 6.816,164.779 13.005,236.077 18.924,218.078 232.099,308.663 268.399,493.918 16.874,86.119 -37.253,229.874 -42.144,323.022 -7.527,143.381 69.579,142.669 104.526,244.648&quot;" fill="none" stroke="#000000" stroke-width="2" />
    </svg>
    
    <textarea id="output"></textarea>

    3. Manual approach

    All you need to know is the number of values/parameters for each command type and split the command values into chunks accordingly:

    M: 2 values - succeeding values are treated as implicit L linetos.
    L, T: 2 values
    V, H: 1 value
    C: 6 values
    S, Q: 4 values
    A: 7 values