平滑连接圆心

4
我试图绘制一条穿过管道内部的路径,沿着中心线。我需要处理的数据是管道每个转弯处描述管道的圆的中心点。在直管段中绘制路径很简单,但我不知道如何处理拐弯。两个圆之间的任何转弯都应该是恒定半径的。因此,我可以访问这个圆上的两个点以及该点处圆线的方向。有人知道我该如何从这些信息计算出其余的圆吗?
编辑:
附上一个逼真的管道草图。
所以假设这条曲线不像地狱一样颤动,蓝色线表示圆,红色线表示中心点,绿色线表示中心路径。

DrawArc函数需要半径(作为边界矩形)和两个角度参数:一个表示起始角度,另一个表示弧线覆盖的角度。例如,对于一个90°的转弯,角度参数应该是90或270,具体取决于方向。 - TaW
嗯,看起来很有用。不过我正在使用另一个绘图工具,所以它不兼容。我需要自己计算实际的圆形。 - djcmm476
更新以添加一个草图。 - djcmm476
1
这可能是一个纯数学问题。你正在寻找如何用曲线连接两条直线的方法。 - Lasse V. Karlsen
好的,圆将与黑色圆共享其中心,并且其半径将位于它们之间。您需要以什么形式? - TaW
显示剩余2条评论
1个回答

6
  1. Clarification

    tube has the same circular diameter everywhere so no distortion due to bending is present !!! input is 2 endpoints points (centers of tube) P0,P1 and 2 vectors (normal/direction of tube) N0,N1

    pipe bend

  2. Solution

    Use Interpolation cubic for example this one

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

    so write equations for the known data, solve a0,a1,a2,a3 coefficients for each axis you need (2D: x,y) and then you can get the center point and its normal in any point along the bend side which is what you need.

    Now some generic equations:

    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 are vectors !!!
    • t is scalar

    Now add the known data

    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
    

    So if I did not make any silly mistake then coefficients are:

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

    So now just encode generic equations into some function with input parameter t and output p(t) and n(t) and/or render circle or tube segment and the call this in for loop for example like this:

    for (t=0.0;t<=1.0;t+=0.1) f(t);
    

[编辑1] 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[]|

这很容易(希望我没有忘记复制什么...)...


@djcmm476 哇,你比我完成编辑得更快 :) 我添加了带有OpenGL可视化的C++代码.... - Spektre
@djcmm476 如果您对曲线形状有特定要求,那么只需为已知数据添加适当的方程(而不是无用的方程),或者添加另一个系数,例如a4等,以便您拥有相同数量的方程和系数。我选择的t范围仅为0-1,以使方程更容易解决。 - Spektre
你也可以通过几何计算来确定方向,方法是将最后两个点 p(t)-p(t-dt) 相减... 我认为这样看起来更好。 - Spektre
我的意思是,你所付出的努力完全值得获得采纳的答案。¬_¬ 我的代码有一些小差异,但我可以调整它使其正常工作。非常感谢! - djcmm476
@djcmm476 大部分工作已经完成,唯一需要做的就是在画图软件中绘制第一张图片 :) - Spektre

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接