二次贝塞尔曲线:计算点
Posted
技术标签:
【中文标题】二次贝塞尔曲线:计算点【英文标题】:Quadratic Bézier Curve: Calculate Points 【发布时间】:2011-08-03 19:35:54 【问题描述】:我想计算二次曲线上的一个点。将它与 html5 的 canvas 元素一起使用。
当我在 javascript 中使用quadraticCurveTo()
函数时,我有一个源点、一个目标点和一个控制点。
我如何计算创建的二次曲线上的一个点,比如说t=0.5
,“只有”知道这三个点?
【问题讨论】:
【参考方案1】:如果有人需要立方形式:
//B(t) = (1-t)**3 p0 + 3(1 - t)**2 t P1 + 3(1-t)t**2 P2 + t**3 P3
x = (1-t)*(1-t)*(1-t)*p0x + 3*(1-t)*(1-t)*t*p1x + 3*(1-t)*t*t*p2x + t*t*t*p3x;
y = (1-t)*(1-t)*(1-t)*p0y + 3*(1-t)*(1-t)*t*p1y + 3*(1-t)*t*t*p2y + t*t*t*p3y;
【讨论】:
【参考方案2】:我编辑了 talkhabis 答案(三次曲线),因此曲线以正确的坐标显示。 (无法评论) 需要更改 Y 坐标 (-p[].y+150)。 (一个新的变量可能是一个更好、更有效的解决方案,但你明白了)
// Apply points to SVG and create the curve and controllers :
var path = document.getElementById('path'),
ctrl1 = document.getElementById('ctrl1'),
ctrl2 = document.getElementById('ctrl2'),
D = 'M ' + p0.x + ' ' + (-p0.y+150) +
'C ' + c0.x + ' ' + (-c0.y+150) +', ' + c1.x + ' ' + (-c1.y+150) + ', ' + p1.x + ' ' + (-p1.y+150);
path.setAttribute('d',D);
ctrl1.setAttribute('d','M'+p0.x+','+(-p0.y+150)+'L'+c0.x+','+(-c0.y+150));
ctrl2.setAttribute('d','M'+p1.x+','+(-p1.y+150)+'L'+c1.x+','+(-c1.y+150));
// Lets test the "Bezier Function"
var t = 0, point = document.getElementById('point');
setInterval(function()
var p = Bezier(p0,c0,c1,p1,t);
point.setAttribute('cx',p.x);
point.setAttribute('cy',-p.y+150);
t += 0.01;
if(t>=1) t=0;
,50);
// OK ... Now tring to get "y" on cruve based on mouse "x" :
var svg = document.getElementById('svg'),
point2 = document.getElementById('point2');
svg.onmousemove = function(e)
var x = (e.pageX - 50)/2,
y = (e.pageY - 50)/2;
// "-50" because of "50px margin" on the left side
// and "/2" because the svg width is 300 units and 600 px => 300 = 600/2
// Get the x,y by mouse x
var p = YBX(p0,c0,c1,p1,x);
point2.setAttribute('cx',p.x);
point2.setAttribute('cy',-p.y+150);
http://jsfiddle.net/u214gco8/1/
我还创建了一些 C 代码来测试三次曲线的结果。只需在 main 函数中输入 X 和 Y 坐标即可。
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
void bezierCurve(int x[] , int y[])
double xu = 0.0 , yu = 0.0 , u = 0.0 ;
int i = 0 ;
for(u = 0.0 ; u <= 1.0 ; u += 0.05)
xu = pow(1-u,3)*x[0]+3*u*pow(1-u,2)*x[1]+3*pow(u,2)*(1-u)*x[2]
+pow(u,3)*x[3];
yu = pow(1-u,3)*y[0]+3*u*pow(1-u,2)*y[1]+3*pow(u,2)*(1-u)*y[2]
+pow(u,3)*y[3];
printf("X: %i Y: %i \n" , (int)xu , (int)yu) ;
int main(void)
int x[] = 0,75,50,300;
int y[] = 0,2,140,100;
bezierCurve(x,y);
return 0;
https://ideone.com/glLXcB
【讨论】:
将 Y 值调整为 150 的原因在哪里?这是一个“固定”的调整,还是在曲线/容器的不同大小中有所不同?【参考方案3】:我创建了这个演示:
// x = a * (1-t)³ + b * 3 * (1-t)²t + c * 3 * (1-t)t² + d * t³
//------------------------------------------------------------
// x = a - 3at + 3at² - at³
// + 3bt - 6bt² + 3bt³
// + 3ct² - 3ct³
// + dt³
//--------------------------------
// x = - at³ + 3bt³ - 3ct³ + dt³
// + 3at² - 6bt² + 3ct²
// - 3at + 3bt
// + a
//--------------------------------
// 0 = t³ (-a+3b-3c+d) + => A
// t² (3a-6b+3c) + => B
// t (-3a+3b) + => c
// a - x => D
//--------------------------------
var A = d - 3*c + 3*b - a,
B = 3*c - 6*b + 3*a,
C = 3*b - 3*a,
D = a-x;
// So we need to solve At³ + Bt² + Ct + D = 0
Full example here
可以帮助别人。
【讨论】:
您的 JSFiddle 示例实际上并没有为 x 显示 y。但我还是试过了。它工作了? 转换为 swift:gist.github.com/eonist/f5bb11533ee52ce24bad3ee47044239a THX! @GitSyncApp 因为cubic
函数。它返回我只使用第一个答案的 3 个答案。见1728.org/cubic.htm
是的,我知道。但这正是我所需要的。在三次贝塞尔图上找到 x 的 y。我的观点是,你的小提琴在 x 轴上是按比例缩放的。可能是浏览器的东西¯_(ツ)_/¯ 这真是太棒了。赞一个!【参考方案4】:
使用二次贝塞尔公式,例如在***页面上找到的Bézier Curves:
在伪代码中,那是
t = 0.5; // given example value
x = (1 - t) * (1 - t) * p[0].x + 2 * (1 - t) * t * p[1].x + t * t * p[2].x;
y = (1 - t) * (1 - t) * p[0].y + 2 * (1 - t) * t * p[1].y + t * t * p[2].y;
p[0]
是起点,p[1]
是控制点,p[2]
是终点。 t
是参数,从0到1。
【讨论】:
在这种情况下乘(加)点意味着您乘(加)每个组件。即3 P = [3 * P.x, 3 * p.y]
和P1 + P2 = [P1.x + P2.x, P1.y + P2.y]
。最后,求平方,将其与自身相乘:x² = x * x
。最后一部分“t ∈ [1,0]”表示 t 应该在 0 和 1 之间。
所以,这意味着:Point.x = (1-t)^2 * P0.x + 2 * (1-t) * t * P1.x + t^2 * P2.x ; Point.y = (1-t)^2 * P0.y + 2 * (1-t) * t * P1.y + t^2 * P2.y;经过测试,它可以工作! =) 谢谢!
@xan:IMO 你应该用一些代码(或伪代码)而不是数学符号来回答,因为这是一个编程问题。
什么是t?什么是 p0、p1 和 p2?
@openfrog, t 给出了点相对于起点和终点的位置的一小部分。它是假设起点和终点总和为 1 的百分比,因此 t 通常是分数。 p0 是您的起点。 p1 是您的控制/锚点。 p2 是你的终点。【参考方案5】:
请注意:如果您使用此处提供的常用公式,则不要期望 t = 0.5 会返回曲线长度一半处的点。在大多数情况下不会。
更多关于here “§23 — 以固定距离间隔追踪曲线” 和here。
【讨论】:
曲线的长度真的很难测量。在 t=0.5 时,如果您假设随机控制点位于中心,则平均而言。但是,请注意,它与大多数变速曲线具有相同的问题。找到中间点通常需要测量曲线的一部分并通过二分搜索找到中心位。它并不是非常典型的需要。但是,值得了解的是,如果您发现所有点都以 t=.1 为增量,它们的长度将不相等。 -- 虽然这与问题无关,但与曲线的性质有很大关系。 @Tatarize:大部分是真的,正如所提供的链接中所解释的那样。一个非常典型的场景是相机或网格沿着恒定速度的路径移动......最终可能会使用从曲线计算的折线并使用二分搜索......以上是关于二次贝塞尔曲线:计算点的主要内容,如果未能解决你的问题,请参考以下文章