平滑地连接圆心

时间:2014-08-07 08:54:32

标签: c# math geometry

我试图沿着中心画一条穿过管子内部的路径。我必须处理的数据是圆圈的中心点,用于描述管道中每个转弯开始和结束时的管道。

通过管道的直线段绘制路径是微不足道的,但我不确定如何接近弯道。两个圆之间的任何转弯应该是恒定的半径。因此,我可以访问此圆上的两个点,以及该点处圆圈线的方向。

有谁知道如何计算这个圈子的其余部分?

编辑:

附上管道的照片般逼真的草图。

It's like I'm in the pipe!

假设曲线没有像地狱一样颤抖,蓝色线条表示圆圈,红色表示中心点,绿色表示穿过中心的路径。

1 个答案:

答案 0 :(得分:4)

  1. <强>澄清

    管子到处都有相同的圆形直径,因此不会因弯曲而产生变形!输入是2个端点(管中心)P0,P1和2个矢量(管的正常/方向)N0,N1

    pipe bend

  2. <强>解决方案

    使用插值立方体,例如这个

    p(t)=a0+a1*t+a2*t*t+a3*t*t*t
    t=<0,1.0>
    

    所以写出已知数据的方程式,求解你需要的每个轴的a0,a1,a2,a3系数(2D:x,y),然后你可以在沿弯曲侧的任何点得到中心点及其法线这就是你需要的。

    现在有一些通用方程式:

    p(t)=a0+a1*t+     a2*t*t+     a3*t*t*t // circle center position
    n(t)=   a1   +2.0*a2*t   +3.0*a3*t*t   // circle orientation
    
    • p,n,a0,a1,a2,a3是矢量!!!
    • t是标量

    现在添加已知数据

    I. t=0 -> p(0)=P0
    P0=a0
    a0=P0
    
    II. t=0 -> n(0)=N0
    N0=a1
    a1=N0
    
    III. t=1 -> p(1)=P1
    P1=a0+a1+a2+a3
    P1=P0+N0+a2+a3
    a2=P1-P0-N0-a3
    
    IV. t=1 -> n(1)=N1
    N1=a1+2.0*a2+3.0*a3
    N1=N0+2.0*(P1-P0-N0-a3)+3.0*a3
    a3=N1+N0-2.0*(P1-P0)
    
    III.
    a2=P1-P0-N0-(N1+N0-2.0*(P1-P0))
    a2=P1-P0-N0-N1-N0+2.0*(P1-P0)
    a2=P1-P0-N1+2.0*(P1-P0-N0)
    a2=3.0*(P1-P0)-N1-2.0*N0
    

    因此,如果我没有犯任何愚蠢的错误,那么系数就是:

    a0=P0
    a1=N0
    a2=3.0*(P1-P0)-N1-2.0*N0
    a3=N1+N0-2.0*(P1-P0)
    

    所以现在只需将通用方程编码为某个函数,输入参数为t,输出p(t)n(t)和/或渲染圆或管段,并将此调用for循环,例如像这样:

    for (t=0.0;t<=1.0;t+=0.1) f(t);
    
  3. [edit1] C ++实施

    //---------------------------------------------------------------------------
    void glCircle3D(double *pos,double *nor,double r,bool _fill)
        {
        int i,n=36;
        double a,da=divide(pi2,n),p[3],dp[3],x[3],y[3];
             if (fabs(nor[0]-nor[1])>1e-6) vector_ld(x,nor[1],nor[0],nor[2]);
        else if (fabs(nor[0]-nor[2])>1e-6) vector_ld(x,nor[2],nor[1],nor[0]);
        else if (fabs(nor[1]-nor[2])>1e-6) vector_ld(x,nor[0],nor[2],nor[1]);
        else                       vector_ld(x,1.0,0.0,0.0);
        vector_mul(x,x,nor);
        vector_mul(y,x,nor);
        vector_len(x,x,r);
        vector_len(y,y,r);
        if (_fill)
            {
            glBegin(GL_TRIANGLE_FAN);
            glVertex3dv(pos);
            }
        else glBegin(GL_LINE_STRIP);
        for (a=0.0,i=0;i<=n;i++,a+=da)
            {
            vector_mul(dp,x,cos(a)); vector_add(p,pos,dp);
            vector_mul(dp,y,sin(a)); vector_add(p,p  ,dp);
            glVertex3dv(p);
            }
        glEnd();
        }
    //---------------------------------------------------------------------------
    void tube(double *P0,double *N0,double *P1,double *N1,double R)
        {
        int i;
        double a0[3],a1[3],a2[3],a3[3],p[3],n[3],t,tt,ttt;
        // compute coefficients
        for (i=0;i<3;i++)
            {
            a0[i]=P0[i];
            a1[i]=N0[i];
            a2[i]=(3.0*(P1[i]-P0[i]))-N1[i]-(2.0*N0[i]);
            a3[i]=N1[i]+N0[i]-2.0*(P1[i]-P0[i]);
            }
        // step through curve from t=0 to t=1
        for (t=0.0;t<=1.0;t+=0.02)
            {
            tt=t*t;
            ttt=tt*t;
            // compute circle position and orientation
            for (i=0;i<3;i++)
                {
                p[i]=a0[i]+(a1[i]*t)+(a2[i]*tt)+(a3[i]*ttt);
                n[i]=a1[i]+(2.0*a2[i]*t)+(3.0*a3[i]*tt);
                }
            // render it
            glCircle3D(p,n,R,false);
            }
        }
    //---------------------------------------------------------------------------
    void test()
        {
        // tube parameters
        double P0[3]={-1.0, 0.0, 0.0},N0[3]={+1.0,-1.0, 0.0},p[3];
        double P1[3]={+1.0,+1.0, 0.0},N1[3]={ 0.0,+1.0, 0.0};
        // just normalize normals to size 3.1415...
        vector_len(N0,N0,M_PI);
        vector_len(N1,N1,M_PI);
        // draw normals to visula confirmation of tube direction
        glBegin(GL_LINES);
        glColor3f(0.0,0.0,1.0); vector_add(p,P0,N0); glVertex3dv(P0); glVertex3dv(p);
        glColor3f(0.0,0.0,1.0); vector_add(p,P1,N1); glVertex3dv(P1); glVertex3dv(p);
        glEnd();
        // render tube
        glColor3f(1.0,1.0,1.0); tube(P0,N0,P1,N1,0.2);
        }
    //---------------------------------------------------------------------------
    

    从视觉上来看,当法线大小为M_PI (3.1415...)时,最好看一下这就是上面代码的查找方式:

    pipe C++

    我的代码使用了我的矢量库,所以你只需要编写如下函数:

    vector_ld(a,x,y,z); //a[]={ x,y,z }
    vector_mul(a,b,c);  //a[]=b[] x c[]
    vector_mul(a,b,c);  //a[]=b[] * c
    vector_add(a,b,c);  //a[]=b[] + c[]
    vector_sub(a,b,c);  //a[]=b[] - c[]
    vector_len(a,b,c);  //a[]=b[]*  c / |b[]|
    

    这很容易(希望我没有忘记复制一些东西......)......