Created
February 27, 2011 04:28
-
-
Save Phrogz/845901 to your computer and use it in GitHub Desktop.
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} |
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")
}
}
}
};
Great work! We've implemented it in svg.topoly.js a plugin for svg.js.
SVGPathElement.getPathSegAtLength is removed from Chrome.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi man, what about adaptive sampling? A friend showed me this and it seems to be more ''efficient''. http://antigrain.com/research/adaptive_bezier/