admin管理员组文章数量:1323342
I'm trying to perform Boolean Operations on SVG Paths (that contain beziers, both quadratic and cubic) using JS Clipper.
JS Clipper starts with polygons then performs the operation and then it seems to convert them back to SVG paths.
The function below gives an SVG path but the below example starts with 2 polygons.
An example function:
// Polygon Arrays are expanded for better readability
function clip() {
var subj_polygons = [
[{
X: 10,
Y: 10
}, {
X: 110,
Y: 10
}, {
X: 110,
Y: 110
}, {
X: 10,
Y: 110
}],
[{
X: 20,
Y: 20
}, {
X: 20,
Y: 100
}, {
X: 100,
Y: 100
}, {
X: 100,
Y: 20
}]
];
var clip_polygons = [
[{
X: 50,
Y: 50
}, {
X: 150,
Y: 50
}, {
X: 150,
Y: 150
}, {
X: 50,
Y: 150
}],
[{
X: 60,
Y: 60
}, {
X: 60,
Y: 140
}, {
X: 140,
Y: 140
}, {
X: 140,
Y: 60
}]
];
var scale = 100;
subj_polygons = scaleup(subj_polygons, scale);
clip_polygons = scaleup(clip_polygons, scale);
var cpr = new ClipperLib.Clipper();
cpr.AddPolygons(subj_polygons, ClipperLib.PolyType.ptSubject);
cpr.AddPolygons(clip_polygons, ClipperLib.PolyType.ptClip);
var subject_fillType = ClipperLib.PolyFillType.pftNonZero;
var clip_fillType = ClipperLib.PolyFillType.pftNonZero;
var clipTypes = [ClipperLib.ClipType.ctUnion];
var clipTypesTexts = "Union";
var solution_polygons, svg, cont = document.getElementById('svgcontainer');
var i;
for (i = 0; i < clipTypes.length; i++) {
solution_polygons = new ClipperLib.Polygons();
cpr.Execute(clipTypes[i], solution_polygons, subject_fillType, clip_fillType);
console.log(polys2path(solution_polygons, scale));
}
}
// helper function to scale up polygon coordinates
function scaleup(poly, scale) {
var i, j;
if (!scale) scale = 1;
for (i = 0; i < poly.length; i++) {
for (j = 0; j < poly[i].length; j++) {
poly[i][j].X *= scale;
poly[i][j].Y *= scale;
}
}
return poly;
}
// converts polygons to SVG path string
function polys2path(poly, scale) {
var path = "",
i, j;
if (!scale) scale = 1;
for (i = 0; i < poly.length; i++) {
for (j = 0; j < poly[i].length; j++) {
if (!j) path += "M";
else path += "L";
path += (poly[i][j].X / scale) + ", " + (poly[i][j].Y / scale);
}
path += "Z";
}
return path;
}
I'm trying to perform Boolean Operations on SVG Paths (that contain beziers, both quadratic and cubic) using JS Clipper.
JS Clipper starts with polygons then performs the operation and then it seems to convert them back to SVG paths.
The function below gives an SVG path but the below example starts with 2 polygons.
An example function:
// Polygon Arrays are expanded for better readability
function clip() {
var subj_polygons = [
[{
X: 10,
Y: 10
}, {
X: 110,
Y: 10
}, {
X: 110,
Y: 110
}, {
X: 10,
Y: 110
}],
[{
X: 20,
Y: 20
}, {
X: 20,
Y: 100
}, {
X: 100,
Y: 100
}, {
X: 100,
Y: 20
}]
];
var clip_polygons = [
[{
X: 50,
Y: 50
}, {
X: 150,
Y: 50
}, {
X: 150,
Y: 150
}, {
X: 50,
Y: 150
}],
[{
X: 60,
Y: 60
}, {
X: 60,
Y: 140
}, {
X: 140,
Y: 140
}, {
X: 140,
Y: 60
}]
];
var scale = 100;
subj_polygons = scaleup(subj_polygons, scale);
clip_polygons = scaleup(clip_polygons, scale);
var cpr = new ClipperLib.Clipper();
cpr.AddPolygons(subj_polygons, ClipperLib.PolyType.ptSubject);
cpr.AddPolygons(clip_polygons, ClipperLib.PolyType.ptClip);
var subject_fillType = ClipperLib.PolyFillType.pftNonZero;
var clip_fillType = ClipperLib.PolyFillType.pftNonZero;
var clipTypes = [ClipperLib.ClipType.ctUnion];
var clipTypesTexts = "Union";
var solution_polygons, svg, cont = document.getElementById('svgcontainer');
var i;
for (i = 0; i < clipTypes.length; i++) {
solution_polygons = new ClipperLib.Polygons();
cpr.Execute(clipTypes[i], solution_polygons, subject_fillType, clip_fillType);
console.log(polys2path(solution_polygons, scale));
}
}
// helper function to scale up polygon coordinates
function scaleup(poly, scale) {
var i, j;
if (!scale) scale = 1;
for (i = 0; i < poly.length; i++) {
for (j = 0; j < poly[i].length; j++) {
poly[i][j].X *= scale;
poly[i][j].Y *= scale;
}
}
return poly;
}
// converts polygons to SVG path string
function polys2path(poly, scale) {
var path = "",
i, j;
if (!scale) scale = 1;
for (i = 0; i < poly.length; i++) {
for (j = 0; j < poly[i].length; j++) {
if (!j) path += "M";
else path += "L";
path += (poly[i][j].X / scale) + ", " + (poly[i][j].Y / scale);
}
path += "Z";
}
return path;
}
Share
Improve this question
edited Jun 27, 2016 at 23:39
nicholaswmin
asked Mar 6, 2013 at 12:45
nicholaswminnicholaswmin
23k16 gold badges101 silver badges173 bronze badges
2 Answers
Reset to default 6I assume that you mean some sort of svg path to polygon conversion.
I have searched a lot, but not found anything reliable and out-of-the-box solution.
SVG path can consist of ten different segment, or 20 if we take into account both relative and absolute coordinates. They are represented as letters in path element's d-attribute: relative ones are mhvlcqastz
and absolute ones are MHVLCQASTZ
. Each have different attributes, a
(elliptical arc) being the most plicated one. The most usable and flexible of types is c
(cubic bezier curve), because it can represent all other types in rather high precision as these examples show: http://jsbin./oqojan/32, http://jsbin./oqojan/42.
Raphael JS library has Path2Curve
-function which can convert all path segments to cubic curves and it can handle also the plicated arc to cubic conversion. Unfortunately it has a bug, so that it cannot handle all possible path segment binations, but fortunately there is a fixed version of library available: http://jsbin./oqojan/32/edit (look at the Javascript-window).
When all path segments are converted to cubic curves, they can be converted to individual line segments. There are few ways, and the best seems to be an adaptive recursive subdivision method, which produces more line segments in sharp turns of curve and fewer in other parts of curve to achieve a balance of curve fidelity and low count of segments to maximize rendering speed, but unfortunately it could not handle all collinear cases. I succeeded in converting AntiGrain's method to Javascript and added presplitting functionality, which splits the curve in local extremes (first derivative roots) and after that the AntiGrain method handles also all possible collinear cases:
Collinear horizontal: http://jsbin./ivomiq/6
Set of different cases: http://jsbin./ivomiq/7
Random: http://jsbin./ivomiq/8
Collinear rotated: http://jsbin./ivomiq/9
All the above samples have two paths in top of each other to show possible errors in adaptive algorithm: the red curve is splitted using very slow brute force method and the green one is splitted using AntiGrain method. If you see not red at all, the AntiGrain's method approximate()-function
is working as expected.
OK, now we have repaired Raphael and repaired AntiGrain. If we bine these both methods, we can create a function that converts ANY svg path element to polygon (single or multiple subpolygons). I'm not 100% sure that this is the best or fastest method, but it should be usable. Of course the best would be native browser implementation...
you can use De Casteljau's algorithm to break bezier curve
into smaller straight lines, and join them to create polygon
.
Here is some references of De Casteljau's algorithm
- http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/de-casteljau.html
- http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/de-casteljau.html
本文标签: Convert an SVGpath to polygons for use within Javascript ClipperStack Overflow
版权声明:本文标题:Convert an SVG-path to polygons for use within Javascript Clipper - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742134026a2422290.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论