给出以下路径(例如)描述SVG三次贝塞尔曲线:"M300,140C300,40,500,40,500,140",并假设连接端点300,140到500,140的直线(关闭曲线下面积),是否可能计算如此封闭的面积?
任何人都可以建议一个公式(或JavaScript)来实现这一目标吗?
Phr*_*ogz 49

以上演示的核心是使用函数自适应地将路径细分为多边形并计算多边形的区域:
// path: an SVG <path> element
// threshold: a 'close-enough' limit (ignore subdivisions with area less than this)
// segments: (optional) how many segments to subdivisions to create at each level
// returns: a new SVG <polygon> element
function pathToPolygonViaSubdivision(path,threshold,segments){
if (!threshold) threshold = 0.0001; // Get really, really close
if (!segments) segments = 3; // 2 segments creates 0-area triangles
var points = subdivide( ptWithLength(0), ptWithLength( path.getTotalLength() ) );
for (var i=points.length;i--;) points[i] = [points[i].x,points[i].y];
var doc = path.ownerDocument;
var poly = doc.createElementNS('http://www.w3.org/2000/svg','polygon');
poly.setAttribute('points',points.join(' '));
return poly;
// Record the distance along the path with the point for later reference
function ptWithLength(d) {
var pt = path.getPointAtLength(d); pt.d = d; return pt;
}
// Create segments evenly spaced between two points on the path.
// If the area of the result is less than the threshold return the endpoints.
// Otherwise, keep the intermediary points and subdivide each consecutive pair.
function subdivide(p1,p2){
var pts=[p1];
for (var i=1,step=(p2.d-p1.d)/segments;i<segments;i++){
pts[i] = ptWithLength(p1.d + step*i);
}
pts.push(p2);
if (polyArea(pts)<=threshold) return [p1,p2];
else {
var result = [];
for (var i=1;i<pts.length;++i){
var mids = subdivide(pts[i-1], pts[i]);
mids.pop(); // We'll get the last point as the start of the next pair
result = result.concat(mids)
}
result.push(p2);
return result;
}
}
// Calculate the area of an polygon represented by an array of points
function polyArea(points){
var p1,p2;
for(var area=0,len=points.length,i=0;i<len;++i){
p1 = points[i];
p2 = points[(i-1+len)%len]; // Previous point, with wraparound
area += (p2.x+p1.x) * (p2.y-p1.y);
}
return Math.abs(area/2);
}
}
Run Code Online (Sandbox Code Playgroud)
// Return the area for an SVG <polygon> or <polyline>
// Self-crossing polys reduce the effective 'area'
function polyArea(poly){
var area=0,pts=poly.points,len=pts.numberOfItems;
for(var i=0;i<len;++i){
var p1 = pts.getItem(i), p2=pts.getItem((i+-1+len)%len);
area += (p2.x+p1.x) * (p2.y-p1.y);
}
return Math.abs(area/2);
}
Run Code Online (Sandbox Code Playgroud)
以下是原始答案,它使用不同的(非自适应)技术将其转换<path>为a <polygon>.

以上演示的核心是使用函数近似多边形的路径并计算多边形的面积.
// Calculate the area of an SVG polygon/polyline
function polyArea(poly){
var area=0,pts=poly.points,len=pts.numberOfItems;
for(var i=0;i<len;++i){
var p1 = pts.getItem(i), p2=pts.getItem((i+len-1)%len);
area += (p2.x+p1.x) * (p2.y-p1.y);
}
return Math.abs(area/2);
}
// Create a <polygon> approximation for an SVG <path>
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){
x+=s.x; y+=s.y;
}else{
x=s.x; y=s.y;
}
var last = points[points.length-1];
if (!last || x!=last[0] || y!=last[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 last = points[points.length-1];
if (!last || pt.x!=last[0] || pt.y!=last[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;
}
Run Code Online (Sandbox Code Playgroud)
nbo*_*eel 11
我犹豫是要发表评论或完整回复.但是,简单的谷歌搜索"区域贝塞尔曲线"导致前三个链接(第一个是同一个帖子),在:
http://objectmix.com/graphics/133553-area-closed-bezier-curve.html
使用发散定理提供封闭形式的解决方案.我很惊讶OP没有找到这个链接.
如果网站出现故障,请复制文本,并将回复的作者Kalle Rutanen记入帐户:
一个有趣的问题.对于2D中的任何分段可微曲线,以下一般过程为您提供曲线/曲线系列内的区域.对于多项式曲线(贝塞尔曲线),您将获得闭合形式的解.
设g(t)为分段可微曲线,0 <= t <= 1.g(t)顺时针定向,g(1)= g(0).
设F(x,y)= [x,y]/2
然后div(F(x,y))= 1,其中div表示发散.
现在,散度定理给出了闭合曲线g(t)内的区域作为沿曲线的线积分:
int(点(F(g(t)),perp(g'(t)))dt,t = 0..1)=(1/2)*int(点(g(t),perp(g') (t)))dt,t = 0..1)
perp(x,y)=( - y,x)
其中int用于集成,'用于区分,点用于点积.必须将积分拼接到与平滑曲线段对应的部分.
现在举个例子.取Bezier度3和一个这样的曲线与控制点(x0,y0),(x1,y1),(x2,y2),(x3,y3).该曲线的积分是:
I:= 3/10*y1*x0 - 3/20*y1*x2 - 3/20*y1*x3 - 3/10*y0*x1 - 3/20*y0*x2 - 1/20*y0*x3 + 3/20*y2*x0 + 3/20*y2*x1 - 3/10*y2*x3 + 1/20*y3*x0 + 3/20*y3*x1 + 3/10*y3*x2
为序列中的每条曲线计算并加起来.总和是曲线包围的区域(假设曲线形成一个循环).
如果曲线只包含一条贝塞尔曲线,那么它必须是x3 = x0和y3 = y0,并且面积为:
面积:= 3/20*y1*x0 - 3/20*y1*x2 - 3/20*y0*x1 + 3/20*y0*x2 - 3/20*y2*x0 + 3/20*y2*x1
希望我没有犯错误.
-
Kalle Rutanen
http://kaba.hilvi.org