计算二次贝塞尔曲线的交点

时间:2014-12-27 04:21:18

标签: javascript html5 canvas trigonometry

这肯定会推动我的知识限制。

是否有计算二次贝塞尔曲线与直线之间交点的公式?

示例:

在下图中,我有P1,P2,C(这是控制点)和X1,X2(我的特定计算只是X轴上的一条直线。)

我想知道的是T的X,Y位置以及红色曲线和黑线交叉点处T处的切线角度。

curve example

在做了一些研究并找到this问题之后,我知道我可以使用:

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;

计算沿曲线任意给定点的X,Y位置。因此,使用它我可以沿着曲线循环遍历一堆点,检查是否有任何在我的交叉X轴上。并从那里尝试计算我的切线角度。但这似乎并不是最好的方法。任何数学专家都知道最好的方法是什么?

我认为这可能比我想要的要复杂得多。

3 个答案:

答案 0 :(得分:4)

enter image description here

二次曲线公式:

y=ax^2+bx+c // where a,b,c are known

行公式:

// note: this `B` is not the same as the `b` in the quadratic formula ;-)

y=m*x+B  // where m,B are known.

曲线&线相交,其中两个方程对于相同的[x,y]是真的

这里带注释的代码和演示:



// canvas vars
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;

// linear interpolation utility
var lerp=function(a,b,x){ return(a+x*(b-a)); };

// qCurve & line defs
var p1={x:125,y:200};
var p2={x:250,y:225};
var p3={x:275,y:100};
var a1={x:30,y:125};
var a2={x:300,y:175};

// calc the intersections
var points=calcQLintersects(p1,p2,p3,a1,a2);

// plot the curve, line & solution(s)
var textPoints='Intersections: ';
ctx.beginPath();
ctx.moveTo(p1.x,p1.y);
ctx.quadraticCurveTo(p2.x,p2.y,p3.x,p3.y);
ctx.moveTo(a1.x,a1.y);
ctx.lineTo(a2.x,a2.y);
ctx.stroke();
ctx.beginPath();
for(var i=0;i<points.length;i++){
  var p=points[i];
  ctx.moveTo(p.x,p.y);
  ctx.arc(p.x,p.y,4,0,Math.PI*2);
  ctx.closePath();
  textPoints+=' ['+parseInt(p.x)+','+parseInt(p.y)+']';
}
ctx.font='14px verdana';
ctx.fillText(textPoints,10,20);
ctx.fillStyle='red';
ctx.fill();

///////////////////////////////////////////////////

function calcQLintersects(p1, p2, p3, a1, a2) {
  var intersections=[];

  // inverse line normal
  var normal={
    x: a1.y-a2.y,
    y: a2.x-a1.x,
  }

  // Q-coefficients
  var c2={
    x: p1.x + p2.x*-2 + p3.x,
    y: p1.y + p2.y*-2 + p3.y
  }

  var c1={
    x: p1.x*-2 + p2.x*2,
    y: p1.y*-2 + p2.y*2,
  }

  var c0={
    x: p1.x,
    y: p1.y
  }

  // Transform to line 
  var coefficient=a1.x*a2.y-a2.x*a1.y;
  var a=normal.x*c2.x + normal.y*c2.y;
  var b=(normal.x*c1.x + normal.y*c1.y)/a;
  var c=(normal.x*c0.x + normal.y*c0.y + coefficient)/a;

  // solve the roots
  var roots=[];
  d=b*b-4*c;
  if(d>0){
    var e=Math.sqrt(d);
    roots.push((-b+Math.sqrt(d))/2);
    roots.push((-b-Math.sqrt(d))/2);
  }else if(d==0){
    roots.push(-b/2);
  }

  // calc the solution points
  for(var i=0;i<roots.length;i++){
    var minX=Math.min(a1.x,a2.x);
    var minY=Math.min(a1.y,a2.y);
    var maxX=Math.max(a1.x,a2.x);
    var maxY=Math.max(a1.y,a2.y);
    var t = roots[i];
    if (t>=0 && t<=1) {
      // possible point -- pending bounds check
      var point={
        x:lerp(lerp(p1.x,p2.x,t),lerp(p2.x,p3.x,t),t),
        y:lerp(lerp(p1.y,p2.y,t),lerp(p2.y,p3.y,t),t)
      }
      var x=point.x;
      var y=point.y;
      // bounds checks
      if(a1.x==a2.x && y>=minY && y<=maxY){  
        // vertical line
        intersections.push(point);
      }else if(a1.y==a2.y && x>=minX && x<=maxX){
        // horizontal line
        intersections.push(point);
      }else if(x>=minX && y>=minY && x<=maxX && y<=maxY){
        // line passed bounds check
        intersections.push(point);
      }
    }
  }
  return intersections;
}
&#13;
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
&#13;
<h4>Calculate intersections of QBez-Curve and Line</h4>
<canvas id="canvas" width=350 height=350></canvas>
&#13;
&#13;
&#13;

答案 1 :(得分:4)

如果您只需要在x方向上具有直线的交点,则您已经知道交点的y坐标。要获得x坐标,请执行以下操作:

  • 您所在行的等式只是y = b
  • 设置它等于beziér函数y(t)的y方程得到你:
    b = (1 - t) * (1 - t) * p[0].y + 2 * (1 - t) * t * p[1].y + t * t * p[2].y
  • 解决* for t得到你:
    t = (p[0].y - p[1].y - sqrt(b*a + p[1].y*p[1].y - p[0].y*p[2].y)) / a
    a = p[0].y - 2*p[1].y + p[2].y
  • 将生成的t插入beziér函数x(t)的x方程中,得到x坐标,然后就完成了。

您可能必须注意一些特殊情况,例如当没有解决方案时,因为平方根的参数可能会变为负数或分母(a)可能变为零,或类似的情况。

如果您需要更多帮助或任意行的交叉点,请发表评论。

(*)我使用wolfram alpha来解决这个等式,因为我很懒:Wolfram alpha solution

答案 2 :(得分:0)

用x坐标计算线的tangθ

那么曲线的(x, y)的交点应该是相同的tangθ

所以解决方案是

a = 从 (line.x,0) 到 (0,0) 的线的 x 距离

(curve.x + a) / curve.y = tangθ(θ可以从与x坐标的线交点得到)