在表面上绘制螺旋曲线

时间:2019-07-11 08:29:22

标签: 3d geometry computational-geometry geometry-surface

我很有趣在表面上绘制螺旋曲线,如下所示: pair of pants

它的等式是:

((1.0-z)*(((x-1.0)*(x-1.0))+y*y-(1.0/3.0))*(((x+1.0)*(x+1.0))+(y*y)-(1.0/3.0)))+(z*((x*x)+(y*y)-(1.0/3.0))) == 0.0

其中

x=<-1.0-sqrt(1/3),+1.0+sqrt(1/3)>
y=<-sqrt(1/3),+sqrt(1/3)>
z=< 0.0,1.0>

我在此网站上找到了等式:

https://www.quora.com/What-is-the-mathematical-expression-which-when-plotted-looks-like-a-pair-of-pants

然后我在StackMath中看到了该帖子,但我不明白答案...:

https://math.stackexchange.com/questions/3267636/drawing-a-pair-of-pants-using-python?answertab=active#tab-top

如果有人对在这种表面上绘制螺旋曲线有任何想法,那将对我有很大帮助。 不要犹豫,向我询问更多有关该问题的信息。 谢谢。

1 个答案:

答案 0 :(得分:0)

分析方法不是攻击此方法的唯一方法(无论如何都不适合此站点),因此还有很多其他选择。但是您没有指定螺旋的外观,因为形状有3个末端,而螺旋只有2个...

为简单起见,我假设使用螺旋形信封(就像您将绳子缠绕在形状上一样)。

那么如何攻击这个。通常的方法是在某些矩形2D区域(纹理)和您的表面之间创建映射2D <--> 3D。通常利用诸如圆柱/球面坐标等形状的拓扑。

另一种选择是将形状切成单独的切片,然后将每个切片处理为2D...。这大大简化了事情。

要将螺旋线应用到某些曲面上,可以将其投影或在参数方程式所定义的螺旋线的实际点上找到形状和方向的交点。

这里使用OpenGL可视化的C ++示例:

//---------------------------------------------------------------------------
List<int> slice;        // start index of each z slice in pnt
List<double> pnt;       // GL_POINTS surface points
List<int> lin;          // LINE_STRIP spiral point indexes
//---------------------------------------------------------------------------
void obj_init()
    {
    const double ry=sqrt(1.0/3.0);
    const double rx=1.0+ry;
    double a,x,y,z,d,N=7.0,da,dx,dy,dz,r,rr,_zero;
    int i,j,i0,i1,ii;
    // get points of analytical surface
    pnt.num=0;
    slice.num=0;
    d=0.02; dz=0.25*d;_zero=1e-2;
    for (z=0.0;z<=1.0;z+=dz,slice.add(pnt.num))
     for (x=-rx;x<=rx;x+=d)
      for (y=-ry;y<=ry;y+=d)
       if (fabs(((1.0-z)*(((x-1.0)*(x-1.0))+y*y-(1.0/3.0))*(((x+1.0)*(x+1.0))+(y*y)-(1.0/3.0)))+(z*((x*x)+(y*y)-(1.0/3.0))))<=_zero)
        {
        pnt.add(x);
        pnt.add(y);
        pnt.add(4.0*z-2.0);
        }
    // spiral line
    lin.num=0;
    da=5.0*M_PI/180.0;
    d=pnt[slice[1]+2]-pnt[slice[0]+2];
    for (a=0.0;a<=2.0*M_PI*N;a+=da)     // go through whole spiral
        {
        z=a/(2.0*M_PI*N);               // z = f(angle,screws)
        z=4.0*z-2.0;

        j=((z+2.0)/d);
        i0=j-1; if (i0<0) i0=0;     if (i0>=slice.num) break;
        i1=j+1; if (i1<0) continue; if (i1>=slice.num) i1=slice.num-1;
        i0=slice.dat[i0];
        i1=slice.dat[i1];

        dx=cos(a);                      // direction vector to the spiral point
        dy=sin(a);
        dz=z;

        for (rr=0.0,i=i0;i<i1;i+=3)         // find point with biggest distance from center and close to a,dx,dy
            {
            x=pnt.dat[i+0];
            y=pnt.dat[i+1];
            r=(x*dx)+(y*dy)+(z*dz);     // dot( (dx,dy,dz) , (x,y,z) )
            if (r>rr){ii=i; rr=r; }     // better point found
            }
        if (ii>=0) lin.add(ii);         // add spiral point to lin[]
        }
    }
//---------------------------------------------------------------------------
void obj_draw()
    {
    int i,j;
    glColor3f(0.3,0.3,0.3);
    glBegin(GL_POINTS);
    for (i=0;i<pnt.num;i+=3) glVertex3dv(pnt.dat+i);
    glEnd();
    glColor3f(0.2,0.9,0.2);
    glBegin(GL_LINE_STRIP);
    for (i=0;i<lin.num;i+=3) glVertex3dv(pnt.dat+lin.dat[i]);
    glEnd();
    }
//---------------------------------------------------------------------------

并预览:

preview

我所做的很简单:

  1. 获得表面点pnt[]

    只需测试某个 BBOX 量中的所有(x,y,z)点,如果该点的方程式结果接近于零,则将其添加到点列表pnt[]中。由于我使用嵌套的for循环,其中z轴为外循环,因此点已按z坐标进行排序,并且slice[]列表中包含每个{{ 1}}分片,因此无需在整个列表中进行慢速搜索,因为我可以直接从z到分片索引。

  2. 计算参数螺旋线

    使用圆柱体参数方程式,如果我知道半径(z)和螺钉数量(x,y,z),则可以为任何t=<0,1>计算1.0。由于N已经是形状的参数,因此我可以使用它代替z ...

  3. 计算螺旋形状之间的交点

    简单地遍历整个螺旋。对于它的每个点,找到与螺旋/形状中心轴具有相同方向并且相距更远的点...这可以通过方向向量之间的简单点积来完成。因此,只需在形状的螺旋计算切片上使用实际测试点的t坐标并记住点积的最大值即可测试其所有点。由于中心轴为z,因此不需要叠加... ...找到的点索引存储在z中供以后使用...

正如您在预览中看到的那样,螺旋形有点像锯齿状。这是由于点的非线性分布。如果在每个切片上创建点的拓扑并通过其连接所有相邻切片,则可以插值任何曲面点,从而消除了此问题。之后,您可以使用更少的点以获得更好的结果,并且还可以渲染这些面,而不仅仅是线框。

另一种选择是使用平均FIR滤波器平滑螺旋点

请注意,在代码中我重新调整了lin[]轴(z)的大小,使其与您链接中的图相匹配...

[Edit1]路径对齐螺旋形

您可以创建曲线/折线或任何描述螺旋/螺旋中心的东西。我太懒了,无法构造这样的控制点,所以我改用形状中心点(每个切片计算为z' = 4*z - 2坐标的0.5*(min+max)坐标,但仅将形状(x,y的一半)用于“裤子”是每片2个管的部分...其余的我只是用二次曲线插补了...

从此过程中,处理螺旋线/螺旋线的所有中心,计算局部TBN矩阵(切线,法线,双法线),其中切线也是中心路径的切线,并且其中一个轴与x<0.0轴对齐因为没有变化(Y),所以螺旋角将对准同一根轴且不会扭曲。由此,只需计算螺旋角(从路径开始的中心弧长就是参数),然后将圆柱坐标应用于y=0基矢量,而不是直接应用于t,b,n坐标。

此后,只需将光线从中心投射到螺旋方向,并在与曲面相交时将点渲染/存储即可...

这里预览结果:

path aligned spiral

以及更新的C ++ / VCL / GL代码:

x,y,z

我也平滑了路径并只计算了一半...其余的只是复制/镜像...

此方法应适用于任何分析表面和中心路径...

我也使用我的动态列表模板,所以:


//--------------------------------------------------------------------------- List<int> slice; // start index of each z slice in pnt List<double> pnt; // GL_POINTS surface points List<double> path; // LINE_STRIP curved helix center points List<double> spir; // LINE_STRIP spiral points //--------------------------------------------------------------------------- void obj_init() { const double ry=sqrt(1.0/3.0); const double rx=1.0+ry; double a,x,y,z,zz,d,N=12.0,da,dx,dy,dz,ex,ey,ez,r,rr,_zero; int i,j,i0,i1,ii; // [get points of analytical surface] pnt.num=0; slice.num=0; d=0.02; dz=0.25*d;_zero=1e-2; for (z=0.0;z<=1.0;z+=dz,slice.add(pnt.num)) for (x=-rx;x<=rx;x+=d) for (y=-ry;y<=ry;y+=d) if (fabs(((1.0-z)*(((x-1.0)*(x-1.0))+y*y-(1.0/3.0))*(((x+1.0)*(x+1.0))+(y*y)-(1.0/3.0)))+(z*((x*x)+(y*y)-(1.0/3.0))))<=_zero) { pnt.add(x); pnt.add(y); pnt.add(4.0*z-2.0); } // [helix center path] as center point of half-slice path.num=0; for (i1=0,j=0;j<slice.num;j++) // all slices { i0=i1; i1=slice.dat[j]; dx=+9; ex=-9; dy=+9; ey=-9; dz=+9; ez=-9; for (i=i0;i<i1;i+=3) // single slice { x=pnt.dat[i+0]; y=pnt.dat[i+1]; z=pnt.dat[i+2]; if (x<=0.0) // just left side of pants { if (dx>x) dx=x; if (ex<x) ex=x; // min,max if (dy>y) dy=y; if (ey<y) ey=y; if (dz>z) dz=z; if (ez<z) ez=z; } } if (dz>0.25) break; // stop before pants join path.add(0.5*(dx+ex)); path.add(0.5*(dy+ey)); path.add(0.5*(dz+ez)); } // smooth by averaging for (j=0;j<20;j++) for (i=3;i<path.num;i+=3) { path.dat[i+0]=0.75*path.dat[i+0]+0.25*path.dat[i-3+0]; path.dat[i+1]=0.75*path.dat[i+1]+0.25*path.dat[i-3+1]; path.dat[i+2]=0.75*path.dat[i+2]+0.25*path.dat[i-3+2]; } // interpolate bridge between pants from last path to (0.0,0.0,0.75) i=path.num-3; dx=path.dat[i+0]; dy=path.dat[i+1]; dz=path.dat[i+2]; for (a=0.0;a<1.0;a+=d) { x=dx*(1.0-a*a); y=dy; z=dz+0.5*a; path.add(x); path.add(y); path.add(z); } // mirror/reverse other half for (i=path.num-3;i>=0;i-=3) { path.add(-path.dat[i+0]); path.add( path.dat[i+1]); path.add( path.dat[i+2]); } // [path aligned spiral line envelope] spir.num=0; _zero=1e-2; d=0.01; double *p1,*p0,t[3],b[3],n[3]; // spiral center (actual,previous), TBN (tangent,binormal,normal,tangent) double u[3],v[3],p[3],dp[3]; vector_sub(p,path.dat+3,path.dat); // mirro first path point vector_sub(p,path.dat,p); p1=p; for (j=0;j<path.num;j+=3) // go through whole path { // path center previous,actual p0=p1; p1=path.dat+j; // TBN basis vectors of the spiral slice vector_sub(t,p1,p0); vector_one(t,t); // tangent direction to next center o npath vector_ld(n,0.0,1.0,0.0); vector_mul(b,n,t); vector_one(b,b); // binormal perpendicular to Y axis (path does not change in Y) and tangent vector_mul(n,t,b); vector_one(n,n); // normal perpendiculer to tangent and binormal // angle from j as parameter and screws N a=N*2.0*M_PI*double(j)/double(path.num-3); // dp = direction to spiral point vector_mul(u,n,sin(a)); vector_mul(v,b,cos(a)); vector_add(dp,u,v); vector_mul(dp,dp,d); // center, step x=p1[0]; dx=dp[0]; y=p1[1]; dy=dp[1]; z=p1[2]; dz=dp[2]; // find intersection between p+t*dp and surface (ray casting) for (r=0.0;r<2.0;r+=d,x+=dx,y+=dy,z+=dz) { zz=(z+2.0)*0.25; if (fabs(((1.0-zz)*(((x-1.0)*(x-1.0))+y*y-(1.0/3.0))*(((x+1.0)*(x+1.0))+(y*y)-(1.0/3.0)))+(zz*((x*x)+(y*y)-(1.0/3.0))))<=_zero) { spir.add(x); spir.add(y); spir.add(z); break; } } } } //--------------------------------------------------------------------------- void obj_draw() { int i,j; glColor3f(0.3,0.3,0.3); glBegin(GL_POINTS); for (i=0;i<pnt.num;i+=3) glVertex3dv(pnt.dat+i); glEnd(); glLineWidth(5.0); glColor3f(0.0,0.0,0.9); glBegin(GL_LINE_STRIP); for (i=0;i<path.num;i+=3) glVertex3dv(path.dat+i); glEnd(); glColor3f(0.9,0.0,0.0); glBegin(GL_LINE_STRIP); for (i=0;i<spir.num;i+=3) glVertex3dv(spir.dat+i); glEnd(); glLineWidth(1.0); } //--------------------------------------------------------------------------- List<double> xxx;相同
double xxx[];xxx.add(5);添加到列表的末尾
5访问数组元素(安全)
xxx[7]访问数组元素(不安全但快速的直接访问)
xxx.dat[7]是数组的实际使用大小
xxx.num清除数组并设置xxx.reset()
xxx.num=0xxx.allocate(100)个项目预分配空间

一些与质量检查相关的内容: