Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Convert a SVG path to a Polygon by sampling the path, but also including all control points in the result. See http://phrogz.net/SVG/convert_path_to_polygon.xhtml
// http://phrogz.net/SVG/convert_path_to_polygon.xhtml
function pathToPolygon(path,samples){
if (!samples) samples = 0;
var doc = path.ownerDocument;
var poly = doc.createElementNS('http://www.w3.org/2000/svg','polygon');
// Put all path segments in a queue
for (var segs=[],s=path.pathSegList,i=s.numberOfItems-1;i>=0;--i) segs[i] = s.getItem(i);
var segments = segs.concat();
var seg,lastSeg,points=[],x,y;
var addSegmentPoint = function(s){
if (s.pathSegType == SVGPathSeg.PATHSEG_CLOSEPATH){
}else{
if (s.pathSegType%2==1 && s.pathSegType>1){
// All odd-numbered path types are relative, except PATHSEG_CLOSEPATH (1)
x+=s.x; y+=s.y;
}else{
x=s.x; y=s.y;
}
var lastPoint = points[points.length-1];
if (!lastPoint || x!=lastPoint[0] || y!=lastPoint[1]) points.push([x,y]);
}
};
for (var d=0,len=path.getTotalLength(),step=len/samples;d<=len;d+=step){
var seg = segments[path.getPathSegAtLength(d)];
var pt = path.getPointAtLength(d);
if (seg != lastSeg){
lastSeg = seg;
while (segs.length && segs[0]!=seg) addSegmentPoint( segs.shift() );
}
var lastPoint = points[points.length-1];
if (!lastPoint || pt.x!=lastPoint[0] || pt.y!=lastPoint[1]) points.push([pt.x,pt.y]);
}
for (var i=0,len=segs.length;i<len;++i) addSegmentPoint(segs[i]);
for (var i=0,len=points.length;i<len;++i) points[i] = points[i].join(',');
poly.setAttribute('points',points.join(' '));
return poly;
}
@nicholaswmin

This comment has been minimized.

Copy link

@nicholaswmin nicholaswmin commented Nov 3, 2013

Hi man, what about adaptive sampling? A friend showed me this and it seems to be more ''efficient''. http://antigrain.com/research/adaptive_bezier/

@deusaquilus

This comment has been minimized.

Copy link

@deusaquilus deusaquilus commented Jul 14, 2014

This will return NaNs if you have SVGPathSegLineto(Vertical/Horizontal)(Abs/Rel) i.e. 'V/H' and 'v/h' elements. Eg. if you have a path that contains c0,2.995,2.776,5.398h85.489c3.431... it will return a 'NaN' for the y choord since there's only an x choord in the element. To fix it, change addSegmentPoint to:

var addSegmentPoint = function(s) {
    if (s.pathSegType == SVGPathSeg.PATHSEG_CLOSEPATH) {

    } else {
        if (s.pathSegType % 2 == 1 && s.pathSegType > 1) {
            // All odd-numbered path types are relative, except PATHSEG_CLOSEPATH (1)
            x += s.x ? s.x : 0;    // if it's a relative vertical i.e. 'v' there's no horizontal coord so use 0 as the horizontal
            y += s.y ? s.y : 0;    // if it's a relative horizontal i.e. 'h' there's no vertical coord so use 0 as the vertical
        } else {
            x = s.x ? s.x : x;    // if it's a absolute vertical i.e. 'V' there's no horizontal coord so keep the horizontal where it was last time
            y = s.y ? s.y : y;    // if it's a absolute horizontal i.e. 'H' there's no vertical coord so keep the vertical where it was last time
        }
        var lastPoint = points[points.length - 1];
        if (!lastPoint || x != lastPoint[0] || y != lastPoint[1]) {
            points.push([x, y]);
            if (isNaN(x) || isNaN(y)) {
                throw new Error("found a choord with a NaN value")
            }
        }
    }
};
@peteruithoven

This comment has been minimized.

Copy link

@peteruithoven peteruithoven commented Jul 30, 2014

Great work! We've implemented it in svg.topoly.js a plugin for svg.js.

@croonerg

This comment has been minimized.

Copy link

@croonerg croonerg commented Jan 8, 2018

SVGPathElement.getPathSegAtLength is removed from Chrome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment