Нарисуйте спиральные кривые на поверхности - PullRequest
1 голос
/ 11 июля 2019

Мне интересно рисовать спиральные кривые на поверхности, которые выглядят так: 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 голосов
/ 12 июля 2019

аналитический подход - не единственный способ атаковать это (не подходит для этого сайта в любом случае), поэтому есть немало других вариантов. Но вы не указали, как должна выглядеть спираль, поскольку есть 3 конца формы, а спираль имеет только 2 ...

Для простоты я предполагаю спиральную оболочку (как вы обматываете веревку вокруг фигуры).

Так как на это напасть? Обычный подход заключается в создании отображения 2D <--> 3D между некоторой прямоугольной 2D-областью (текстурой) и вашей поверхностью. Обычно используется топология формы, например, цилиндрические / сферические координаты и т. Д.

Другой вариант - нарезать вашу фигуру на отдельные кусочки и обрабатывать каждый кусочек как 2D ... Что значительно упрощает процесс.

Чтобы применить спираль к некоторой изогнутой поверхности, вы либо проецируете ее, либо находите пересечение между формой и направлением к фактической точке на спирали, определенной параметрическим уравнением.

Вот пример C ++ с использованием визуализации OpenGL:

//---------------------------------------------------------------------------
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[]

    просто протестируйте все (x,y,z) точки в некотором BBOX объеме, и если результат уравнения для точки близок к нулю, я добавлю его в список точек pnt[]. Благодаря тому, что я использую вложенные циклы for с внешней осью z, точки уже отсортированы по координате z, а список slice[] содержит начальные индексы точек для каждого среза z, поэтому нет необходимости медленный поиск по всему списку, поскольку я могу перейти от z к индексу срезов напрямую.

  2. вычислить параметрическую спираль

    Используя параметрическое уравнение цилиндра, я могу вычислить x,y,z для любого t=<0,1> Если я знаю радиус (1.0) и количество винтов (N). Поскольку z уже является параметром фигуры, я могу использовать его вместо t ...

  3. вычисление пересечения между спиралью и формой

    просто перебрать всю спираль. Для каждой из ее точек найдите точку, которая имеет одинаковое направление от центральной оси спирали / формы и находится дальше ... Это можно сделать простым произведением точек между векторами направления. Так что просто используя z координату фактической тестируемой точки на спиральном вычислении среза фигуры и протестируйте все ее точки, помня максимальное значение точечного произведения. Поскольку центральная ось равна z, суперпозиции не нужны ... Индексы найденных точек сохраняются в lin[] для последующего использования ...

Как вы можете видеть на превью, спираль немного зазубренна. Это связано с нелинейным распределением точек. Если вы создаете топологию точек на каждом срезе и соединяете все соседние срезы, вы можете интерполировать любую точку поверхности, устраняя эту проблему. После этого вы можете использовать гораздо меньше точек с еще лучшим результатом, а также визуализировать грани вместо просто каркасной.

Другой вариант - просто сгладить точки спирали, используя усредняющий FIR-фильтр

Остерегайтесь кода, который я изменил масштаб оси z (z' = 4*z - 2), чтобы он соответствовал сюжету в вашей ссылке ...

[Edit1] спираль с выравниванием по траектории

Вы можете создать кривую / полилинию или что-либо еще, описывающее центр спирали / спирали. Мне было лень строить такие контрольные точки, поэтому вместо этого я использовал центральные точки фигуры (вычисленные как 0.5*(min+max) из x,y координаты для каждого среза, но только для половины формы (x<0.0) для частей, где "брюки") 2 трубки ... на срез. Остальное я просто интерполировал по квадратичной кривой ...

Из этого просто обработайте все центры спирали / спирали, вычислите локальную матрицу TBN (касательную, нормальную, бинормальную), где касательная также является касательной к траектории центров, а одна из осей совмещена с осью Y, так как без изменений (y=0), поэтому угол спирали будет выровнен по одной оси и не скручен. Из этого просто вычислите угол спирали (длина центральной линии от начала пути является параметром) и примените цилиндрические координаты к t,b,n базисным векторам, а не непосредственно к x,y,z координатам.

После этого просто направьте луч из центра в направлении спирали, а при попадании на пересечение с поверхностью визуализируйте / сохраните точку ...

Вот предварительный просмотр результата:

path aligned spiral

И обновленный код C ++ / VCL / GL:

//---------------------------------------------------------------------------
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=0
xxx.allocate(100) предварительно выделить место для 100 элементов

Некоторые связанные показатели качества:

...