我正在尝试找到该问题的直接答案,但是我发现的答案通常非常混乱,或者需要比所需逻辑更复杂的逻辑。我的问题基于以下示例:
假设我有一系列观点。它们都是非常温顺且可预测的,因为它们始终采用数学曲线模式...(它们始终从左下开始,从右上结束)
如何确定PHP中此曲线下的面积?
请注意,我尝试使用Canvas在Javascript中复制此示例,但失败了(使用this之类的示例)
<?php
//Example requires Imagick
$width = 800;
$height = 200;
$img = new Imagick();
$img->newImage( $width, $height, new ImagickPixel( 'transparent' ) );
$draw = new ImagickDraw();
$draw->setStrokeColor( new ImagickPixel( 'red' ) );
$draw->setStrokeWidth(4 );
$draw->setFillColor( new ImagickPixel( 'transparent' ) );
$points = array
(
array( 'x' => 0, 'y' => 200 ),
array( 'x' => 100, 'y' => 0 ),
array( 'x' => 200, 'y' => 200 ),
array( 'x' => 300, 'y' => 0 ),
array( 'x' => 400, 'y' => 10 ),
array( 'x' => 500, 'y' => 0 )
);
$draw->bezier($points);
$img->drawImage( $draw );
$img->setImageFormat( "png" );
header( "Content-Type: image/png" );
echo $img;
?>
我知道这个问题可能要花一些迭代的时间问我。。。我会发布一个JSFiddle来备份此示例并使其更易于使用,但是我无法将其转换为与js / bezierCurveTo一起使用,所以如果用户可以帮助它,它也是一个非常有用的替代品
最佳答案
MBo的解决方案将给出确切的答案,您也可以尝试使用数值解决方案。
由于您的曲线总是在x方向上增加,因此我们可以在x方向上对其进行切片,并近似于每个切片的面积。例如,如果曲线上有四个点(x0,y0),(x1,y1),(x2,y2),(x3,y3),则使用
(x1-x0)*(y0+y1)/2
这是梯形的区域。对每对点都做同样的事情,并将它们加起来。如果您的x坐标均匀分布,则将给出梯形规则,我们可以简化该规则。我们可以在这里假设我们正在使用贝塞尔曲线。
如果我们有Bezier曲线,则事情会有些棘手,因为我们实际上并不知道曲线上的点。 MBo给出了积分的公式,一切都没有丢失
X(t) = P[0].X*(1-t)^3+3*P[1].X*t*(1-t)^2+3*P[2].X*t^2*(1-t)+P[3].X*t^3
Y(t) = P[0].Y*(1-t)^3+3*P[1].Y*t*(1-t)^2+3*P[2].Y*t^2*(1-t)+P[3].Y*t^3
P [0] .X是第一个控制点的x坐标,P [0] .Y是Y值,等等。在代码中,您需要使用
x = P[0].X*(1-t)*(1-t)*(1-t)+3*P[1].X*t*(1-t)*(1-t)+3*P[2].X*t*t*(1-t)+P[3].X*t*t*t;
y = P[0].Y*(1-t)*(1-t)*(1-t)+3*P[1].Y*t*(1-t)*(1-t)+3*P[2].Y*t*t*(1-t)+P[3].Y*t*t*t;
它使用乘法而不是幂。使用0到1之间的多个t值来找到曲线上的点,然后找到切片。
我放了一个JavaScript提琴,将所有这些放在一起http://jsfiddle.net/SalixAlba/QQnvm/
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// The control points
var P = [{X: 13, Y: 224 },
{X: 150, Y: 100 },
{X: 251, Y: 224 },
{X: 341, Y: 96 }, ];
ctx.lineWidth = 6;
ctx.strokeStyle = "#333";
ctx.beginPath();
ctx.moveTo(P[0].X, P[0].Y);
ctx.bezierCurveTo(P[1].X, P[1].Y, P[2].X, P[2].Y, P[3].X, P[3].Y);
ctx.stroke();
// draw the control polygon
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(P[0].X, P[0].Y);
ctx.lineTo(P[1].X, P[1].Y);
ctx.lineTo(P[2].X, P[2].Y);
ctx.lineTo(P[3].X, P[3].Y);
ctx.stroke();
function findarea(n) {
ctx.lineWidth = 3;
ctx.strokeStyle = "#f00";
ctx.beginPath();
var nSteps = n - 1;
var x = [P[0].X];
var y = [P[0].Y];
ctx.moveTo(x[0], y[0]);
var area = 0.0;
for (var i = 1; i <= nSteps; ++i) {
var t = i / nSteps;
x[i] = P[0].X*(1-t)*(1-t)*(1-t)+3*P[1].X*t*(1-t)*(1-t)+3*P[2].X*t*t*(1-t)+P[3].X*t*t*t;
y[i] = P[0].Y*(1-t)*(1-t)*(1-t)+3*P[1].Y*t*(1-t)*(1-t)+3*P[2].Y*t*t*(1-t)+P[3].Y*t*t*t;
ctx.lineTo(x[i], y[i]);
area += (x[i] - x[i-1]) * (y[i-1] + y[i]) / 2;
if(x[i]<x[i-1]) alert("Not strictly increasing in x, area will be incorrect");
}
ctx.stroke();
$("#area").val(area);
}
$("#goBut").click(function () {
findarea($("#nPts").val());
});
关于javascript - 计算贝塞尔曲线下的面积,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24724653/