請原諒我的長代碼示例,但我無法弄清楚如何用更少的代碼正確解釋我的問題:
let c = document.querySelector("canvas");
let ctx = c.getContext("2d");
class BezierCurve {
constructor(x1, y1, cpX, cpY, x2, y2) {
this.f = 0;
this.x1 = x1;
this.y1 = y1;
this.cpX = cpX;
this.cpY = cpY;
this.x2 = x2;
this.y2 = y2;
this.pointCache = this.calcPoints();
}
calcX(t) { return (1 - t) * (1 - t) * this.x1 2 * (1 - t) * t * this.cpX t * t * this.x2; }
calcY(t) { return (1 - t) * (1 - t) * this.y1 2 * (1 - t) * t * this.cpY t * t * this.y2; }
calcPoints() {
const step = 0.001, segments = [];
for (let i = 0; i <= 1 - step; i = step) {
let dx = this.calcX(i) - this.calcX(i step);
let dy = this.calcY(i) - this.calcY(i step);
segments.push(Math.sqrt(dx * dx dy * dy));
}
const len = segments.reduce((a, c) => a c, 0);
let result = [], l = 0, co = 0;
for (let i = 0; i < segments.length; i ) {
l = segments[i];
co = step;
result.push({ t: l / len, co });
}
return result;
}
draw() {
ctx.beginPath();
ctx.moveTo(this.x1, this.y1);
ctx.quadraticCurveTo(this.cpX, this.cpY, this.x2, this.y2);
ctx.stroke();
}
tick(amount = 0.001) {
this.f = this.f < 1 ? this.f amount : 0;
}
}
function drawCircle(x, y, r) {
ctx.beginPath();
ctx.arc(x, y, r, 0, 2 * Math.PI);
ctx.fill();
}
let a = new BezierCurve(25, 25, 80, 250, 100, 50);
let b = new BezierCurve(225, 25, 280, 250, 300, 50);
function draw(curve, fraction) {
let x = curve.calcX(fraction);
let y = curve.calcY(fraction);
curve.draw();
drawCircle(x, y, 5);
curve.tick();
}
// Inefficient but using this instead of binary search just to save space in code example
function findClosestNumInArray(arr, goal) {
return arr.reduce((prev, cur) => Math.abs(cur.t - goal) < Math.abs(prev.t - goal) ? cur : prev);
}
function drawLoop(elapsed) {
c.width = 600;
c.height = 600;
draw(a, a.f);
let closest = findClosestNumInArray(b.pointCache, b.f).co;
draw(b, closest);
requestAnimationFrame(drawLoop);
}
drawLoop(0);
<canvas></canvas>
好的,所以,解釋一下發生了什么:如果您點擊,Run code snippet您會看到有兩條曲線,我將其稱為a(左側)和b(右側)。
您可能會注意到沿著 a 曲線移動的點開始很快,然后在曲線周圍減速,然后再次加速。盡管小數部分0.001每幀增加一個常數。
b另一方面,for 的點在整個迭代程序中以恒定速度移動。這是因為b我使用了pointCache我為曲線預先計算的映射。此函式calcPoints生成一個映射,使得輸入小數分量t與沿曲線的“適當”實際百分比相關聯co。
無論如何,這一切都有效,但我的問題是預計算calcPoints很昂貴,并且參考查找表來查找沿線的實際小數部分是不準確的,并且需要大量記憶體使用。我想知道是否有更好的方法。
What I'm looking for is a way to do something like curve.calcX(0.5) and actually get the 50% mark along the curve. Because currently the existing equation does not do this, and I instead have to do this costly workaround.
uj5u.com熱心網友回復:
不完全確定這會起作用,但你知道霍納繪制貝塞爾點的方案嗎?
/***************************************************************************
//
// This routine plots out a bezier curve, with multiple calls to hornbez()
//
//***************************************************************************
function bezierCalculate(context, NumberOfDots, color, dotSize) {
// This routine uses Horner's Scheme to draw entire Bezier Line...
for (var t = 0.0; t < 1.0001; t = t 1.0 / NumberOfDots) {
xTemp = hornbez(numberOfControlPoints - 1, "x", t);
yTemp = hornbez(numberOfControlPoints - 1, "y", t);
drawDot(context, xTemp, yTemp, dotSize, color);
}
}
//***************************************************************************
//
// This routine uses Horner's scheme to compute one coordinate
// value of a Bezier curve. Has to be called
// for each coordinate (x,y, and/or z) of a control polygon.
// See Farin, pg 59,60. Note: This technique is also called
// "nested multiplication".
// Input: degree: degree of curve.
// coeff: array with coefficients of curve.
// t: parameter value.
// Output: coordinate value.
//
//***************************************************************************
function hornbez(degree, xORy, t) {
var i;
var n_choose_i; /* shouldn't be too large! */
var fact, t1, aux;
t1 = 1 - t;
fact = 1;
n_choose_i = 1;
var aux = FrameControlPt[0][xORy] * t1;
/* starting the evaluation loop */
for (i = 1; i < degree; i ) {
fact = fact * t;
n_choose_i = n_choose_i * (degree - i 1) / i; /* always int! */
aux = (aux fact * n_choose_i * FrameControlPt[i][xORy]) * t1;
}
aux = aux fact * t * FrameControlPt[degree][xORy];
return aux;
}
不確定你到底要去哪里,但這是我前段時間寫的一些東西的參考......對于Bezier iframe的內容,請看這個......我隱含的問題?貝塞爾曲線適合您嗎?
uj5u.com熱心網友回復:
我們可以嘗試修改您的方法以提高效率。它仍然不是您希望的確切解決方案,但它可能會起作用。
我們可以使用細分的思想,而不是在相差 0.001 的引數值(您不重復使用上一步的計算)時重復評估貝塞爾曲線。你知道De Casteljau 演算法嗎?它不僅計算給定引數t 下的 Bézier 曲線,還為您提供了將曲線細分為兩條的方法:一條 Bézier 曲線等于區間 [0, t ] 上的原始曲線,另一條等于原始曲線在 [ t , 1] 上。它們的控制多邊形比原始控制多邊形更接近曲線。
因此,您將按以下步驟進行:
- 使用 De Casteljau 演算法細分t =0.5處的曲線。
- 使用 De Casteljau 演算法細分t = 0.25處的第一段。
- 使用 De Casteljau 演算法在t =0.75處細分第二段。
- 以相同的方式遞回進行直到規定的深度。這取決于您想要達到的精度。
這些線段的控制多邊形將是原始貝塞爾曲線的(分段線性)近似值。使用它們來預先計算引數,就像您到目前為止所做的那樣;或直接繪制此近似值,而不是使用quadraticCurveTo原始曲線。生成此近似值應該比您的程式快得多。
您可以在Prautzsch、Boehm 和 Paluszny:Bézier 和 B 樣條技術 的第 3.3、3.4 和 3.5 節中閱讀有關此想法的更多資訊。它們還提供了此程序收斂到原始曲線的速度的估計。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/350522.html
標籤:javascript algorithm bezier spline
上一篇:如何使用big-O表示法計算兩個嵌套回圈的運行時間?
下一篇:求解兩個未知變數和兩個函式
