Support for variable thickness stroke

Viewed 357

Are there any vector graphics standards that support variable-thickness paths / strokes, e.g. from a stylus input:

enter image description here

Some amount of smoothing may be acceptable. I'd assume that the best way to store it would be as a regular path (e.g. this) and then point-wise sparse thickness information at various points in the path, with gradients between them.

I have looked at SVG but there doesn't seem to be an element that can support it. Are there any vector graphics standards that can?

2 Answers

A single path as currently implemented does not allow variable thickness. There is a W3.org proposal for SVG standard, but no implementation so far in pure SVG.

There are several implementation of a "path with variable thickness", but that relies on svg objects (eg., multiple paths) and a c++ or javascript functions.

  • PowerStroke is an implementation of such idea of a variable thickness stroke in Inkscape. A good entry to the source in c++ is here.

There are other implementations in SVG and javascript, relying on multiple paths:

  • Tubefy, a set of few js functions, the principle is based on a linear interpolation. There are several implementation of Tubefy, the simplest is:

     $ = function (id) { return typeof id=='string'?document.getElementById(id):id };
     var root = document.rootElement;
    
     function lerp(p, a, b) { return Number(a)+(b-a)*p; }
    
     function lerpA(p, a, b) { var c=[];
         for(var i=0; i<a.length; i++) c[i]=lerp(p, a[i], b[i]);
         return c;
     }
    
     function toCss(a){ 
         for(var i=0; i<a.length; i++) a[i]=Math.round(a[i]);
         return "rgb(" + a.join() + ")";
     }
    
  • Variable Stroke-Width, based on multiple path, which could be the best answer to your needs.

In one of the examples, the js function uses Tubefy and is directly implemented in the svg file:

<script>//<![CDATA[
    var op=1, op1=1;

    function vsw0(p0, n, g){    p0=$(p0);
        var SW=p0.getAttribute('stroke-widths').replace(/ /g,'').split(',');
        var T=p0.getTotalLength();
        var n_1=n-1,  dt=T/n, dash=(dt+1)+','+T;
        p0.setAttribute('stroke-dasharray', dash);

        for(var i=0; i<n; i++){ p=i/n_1;
            var sw=lerp(p, SW[0], SW[1]); // current stroke width
            var off=-i*dt; // current dash offset
            var c=toCss(lerpA(p, [255,0,0], [255,255,0])); // curr color

            var newP=p0.cloneNode(true);
            newP.setAttribute('style', 'stroke-width:'+sw+';stroke-dashoffset:'+off+';stroke:'+c);
            $(g).appendChild(newP);
        }
    }

    function f(){ $('abg').setAttribute('stroke', $('bg').getAttribute('fill')) }

//]]></script>
</svg>

enter image description here

enter image description here

Unfortunately this has been proposed but not further developed as an SVG standard:

https://www.w3.org/Graphics/SVG/WG/wiki/Proposals/Variable_width_stroke

Your best bet would be to generate your own outline curve based on the desired inner curve and stroke widths.

Adobe Illustrator does this when using their width tool, and Inkscape has a feature which does that too.

So technically to answer your question, the .ai file format does save stroke width information, but when exported to SVG it is a closed path with fill.

Related