簡體   English   中英

計算貝塞爾曲線下的面積

[英]Calculate area under Bezier curve

我正在嘗試找到該問題的直接答案,但是我發現的答案通常非常復雜,或者需要比所需邏輯更復雜的邏輯。 我的問題基於以下示例:

假設我有一系列觀點。 它們都是非常溫順且可預測的,因為它們始終采用數學曲線模式... (它們始終從左下開始,從右上結束)

在此處輸入圖片說明

如何確定PHP中此曲線下的面積?

請注意,我嘗試使用Canvas在Javascript中復制此示例,但不幸失敗(使用類似這樣的示例)

<?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());
});

您可以使用公式計算參數曲線下的面積

A = Integral[t0..t1] (y(t)*x'(t)*dt)

對於三次方貝塞爾曲線:(我不確定您使用的是哪種貝塞爾曲線)

Area = Integral[0..1](y(t)*x'(t)*dt)=
Integral[0..1](
(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*(1-t)^3+3*P[1].X*t*(1-t)^2+3*P[2].X*t^2*(1-t)+P[3].X*t^3)'*
dt)

您必須展開方括號,區分第二行表達式,將表達式相乘並積分結果

楓木作品(很難復制沒有扭曲的文本): 在此處輸入圖片說明 請注意,某些表達式已多次使用

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM