He estado atrapado en esto durante una semana, ahora parece que no puedo resolverlo.

Tengo un arco que puedo convertir en una serie de curvas bezier con bastante facilidad cuando el arco es plano:

enter image description here

Pero estoy luchando por descubrir cómo encontrar las curvas de Bezier cuando el arco es una hélice y las tangentes finales tienen diferentes pendientes.

Esto es lo más lejos que he llegado hasta ahora:

enter image description here

Como puede ver, cada curva de Bezier tiene puntos de control que no están en el plano correcto, y la tangente inicial y final (los vectores rojos en la segunda imagen) del arco completo no se tienen en cuenta, ya que no pude averiguar cómo hazlo.

Para encontrar la versión plana de los cortes bezier de arcos, tengo este código que ciertamente funciona bien para un arco plano:

    // from https://pomax.github.io/bezierinfo/#circles_cubic
    public CubicBezier ConvertArc(Vector3 origin, float radius, Vector3 from, Vector3 to, float angle)
    {
        var c = Math.Tan(angle * Mathf.Deg2Rad / 4f) * 4 / 3f * radius;

        var c1 = from + (from - origin).Perp().normalized * c;
        var c2 = to - (to - origin).Perp().normalized * c;
        return new CubicBezier(from, c1, c2, to);
    }

Este es mi código actual para crear cada corte bezier:

        //cut the arc in to bezier curves up to 90 degrees max
        float cuts = _arc.totalAngle / 90f;
        for (int i = 0; i < cuts; i++)
        {
            float t = i / cuts;
            float t2 = (i + 1) / cuts;

            Arc slice = new Arc(_arc,_arc.Point(t),_arc.Point(t2));

            //this function below is the issue, it needs start and end tangent for the slice, 
            //but i also don't know how to find the tangents at each slice for the whole arc
            //relating the start and end tangents of the entire arc
            //see above snippet for function code
            var cb = ConvertArc(slice.origin, slice.radius, slice.a, slice.b, slice.totalAngle);
            cb.DebugDraw(Color.yellow);
        }

Espero que alguien pueda ayudar a explicar la lógica para resolver cómo encontrar los puntos de control correctamente para que coincidan con las tangentes, desperdiciadas una semana ya con poco progreso.

Esto está escrito en C # pero no creo que el idioma importe, las matemáticas son matemáticas sin importar el idioma.

1
WDUK 7 may. 2020 a las 10:42

2 respuestas

Tiene algún segmento de curva 3d con tangentes conocidas en los puntos finales y desea construir una aproximación de Bezier.

Los puntos de control internos de la curva de Bezier se ubicarán en vectores colineales con vectores tangentes. Pero necesitas saber su longitud.

El enfoque de aproximación para el arco circular elige la longitud de estos vectores para proporcionar un punto Bezier medio que coincida con el punto medio del arco. Puede aplicar el mismo método aquí. Escribir

P1 = P0 + T0 * L
P2 = P3 - T3 * L

Sustituya en la ecuación de Bezier con t = 1/2, P = centro de la curva y encuentre L. desconocido. Haga esto para los tres componentes y obtenga un promedio que proporcione un error bastante bueno (tal vez sea posible alguna optimización).

Si la curva es altamente asimétrica, alguien puede intentar usar diferentes longitudes para ambas tangentes.

0
MBo 7 may. 2020 a las 11:42

El problema es que los puntos de control de Bezier no son tan intuitivos como los cúbicos de interpolación. Entonces podemos usarlos en su lugar y convertir sus puntos de control en bezier más tarde para facilitar las cosas.

  1. Simplemente cree una lista de puntos a lo largo de su ruta

    todos estos están directamente en el camino y la continuidad de la curva está garantizada por la ecuación cúbica de interpolación en sí misma, por lo que no es necesario ajustar ...

    asegúrese de tener suficientes puntos ... por ejemplo, para un círculo completo se necesitan al menos 8 puntos, la tuerca 16 es mejor ...

  2. Convertir puntos de ruta en puntos de control cúbicos de Bezier

    así que simplemente elija 4 puntos consecuentes en el camino y conviértalos en puntos de control más precisos usando esto:

    para garantizar la continuidad, el próximo bezier debe hacerse desde el siguiente punto ... Entonces, si tenemos los puntos p0, p1, p2, p3, p4, p5 ... entonces creamos beziers desde (p0,p1,p2,p3), (p1,p2,p3,p4) , ... y así. El primer punto p0 determina la dirección inicial y el último el final. Si desea que su ruta comience / termine en esos simplemente duplíquelos ...

Aquí un pequeño ejemplo no optimizado y crudo de esto en C ++:

//---------------------------------------------------------------------------
List<double> it4;   // interpolation cubic control points
List<double> bz4;   // bezier cubic control points
//---------------------------------------------------------------------------
void generate()
    {
    int i,j,n;
    double x,y,z,a,a0,a1,z0,z1,da,dz,r;
    const double deg=M_PI/180.0;
    const double rad=180.0/M_PI;

    // generate some helix path points
    n=32;                           // number of points along path
    r=0.75;                         // radius
    z0=0.0; z1=0.5;                 // height range
    a0=-25.0*deg; a1=+720.0*deg;    // angle range
    da=(a1-a0)/double(n);
    dz=(z1-z0)/double(n);
    it4.num=0;  // clear list of points
    for (z=z0,a=a0,i=0;i<n;i++,a+=da,z+=dz)
        {
        // 3D point on helix
        x=r*cos(a);
        y=r*sin(a);
        // add it to the list
        it4.add(x);
        it4.add(y);
        it4.add(z);
        }

    // convert it4 into bz4 control points
    bz4.num=0;  // clear list of points
    for (i=0;i<=it4.num-12;i+=3)
        {
        const double m=1.0/6.0;
        double x0,y0,z0,x1,y1,z1,x2,y2,z2,x3,y3,z3;
        double X0,Y0,Z0,X1,Y1,Z1,X2,Y2,Z2,X3,Y3,Z3;
        j=i;
        X0=it4[j]; j++; Y0=it4[j]; j++; Z0=it4[j]; j++;
        X1=it4[j]; j++; Y1=it4[j]; j++; Z1=it4[j]; j++;
        X2=it4[j]; j++; Y2=it4[j]; j++; Z2=it4[j]; j++;
        X3=it4[j]; j++; Y3=it4[j]; j++; Z3=it4[j]; j++;
        x0 = X1;           y0 = Y1;           z0 = Z1;
        x1 = X1-(X0-X2)*m; y1 = Y1-(Y0-Y2)*m; z1 = Z1-(Z0-Z2)*m;
        x2 = X2+(X1-X3)*m; y2 = Y2+(Y1-Y3)*m; z2 = Z2+(Z1-Z3)*m;
        x3 = X2;           y3 = Y2;           z3 = Z2;
        bz4.add(x0); bz4.add(y0); bz4.add(z0);
        bz4.add(x1); bz4.add(y1); bz4.add(z1);
        bz4.add(x2); bz4.add(y2); bz4.add(z2);
        bz4.add(x3); bz4.add(y3); bz4.add(z3);
        }
    }
//---------------------------------------------------------------------------

Y render simple en VCL / GL / C ++

//---------------------------------------------------------------------------
void gl_draw()
    {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    float aspect=float(xs)/float(ys);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0/aspect,aspect,0.1,100.0);
    glMatrixMode(GL_TEXTURE);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.0,0.0,-2.5);
    glRotatef(-70.0,1.0,0.0,0.0);
    glRotatef(-130.0,0.0,0.0,1.0);

    glEnable(GL_DEPTH_TEST);
    glDisable(GL_TEXTURE_2D);

    int i,j;
    // render axises
    glBegin(GL_LINES);
    glColor3f(1.0,0.0,0.0); glVertex3d(1.0,0.0,0.0); glVertex3d(0.0,0.0,0.0);
    glColor3f(0.0,1.0,0.0); glVertex3d(0.0,1.0,0.0); glVertex3d(0.0,0.0,0.0);
    glColor3f(0.0,0.0,1.0); glVertex3d(0.0,0.0,1.0); glVertex3d(0.0,0.0,0.0);
    glEnd();


    // render it4 control points (aqua)
    glColor3f(0.0,1.0,1.0);
    glPointSize(8);
    glBegin(GL_POINTS);
    for (i=0;i<it4.num;i+=3) glVertex3dv(it4.dat+i);
    glEnd();
    glPointSize(1);

    // render bz4 control points (magenta)
    glColor3f(1.0,0.0,1.0);
    glPointSize(4);
    glBegin(GL_POINTS);
    for (i=0;i<bz4.num;i+=3) glVertex3dv(bz4.dat+i);
    glEnd();
    glPointSize(1);

    // render bz4 path (yellow)
    double t,tt,ttt,cx[4],cy[4],cz[4],x,y,z;
    double x0,y0,z0,x1,y1,z1,x2,y2,z2,x3,y3,z3;
    glColor3f(1.0,1.0,0.0);
    glLineWidth(2);
    for (i=0;i<=bz4.num-12;i+=12)
        {
        j=i;
        x0=bz4[j]; j++; y0=bz4[j]; j++; z0=bz4[j]; j++;
        x1=bz4[j]; j++; y1=bz4[j]; j++; z1=bz4[j]; j++;
        x2=bz4[j]; j++; y2=bz4[j]; j++; z2=bz4[j]; j++;
        x3=bz4[j]; j++; y3=bz4[j]; j++; z3=bz4[j]; j++;
        cx[0]=                            (    x0);
        cx[1]=                   (3.0*x1)-(3.0*x0);
        cx[2]=          (3.0*x2)-(6.0*x1)+(3.0*x0);
        cx[3]= (    x3)-(3.0*x2)+(3.0*x1)-(    x0);
        cy[0]=                            (    y0);
        cy[1]=                   (3.0*y1)-(3.0*y0);
        cy[2]=          (3.0*y2)-(6.0*y1)+(3.0*y0);
        cy[3]= (    y3)-(3.0*y2)+(3.0*y1)-(    y0);
        cz[0]=                            (    z0);
        cz[1]=                   (3.0*z1)-(3.0*z0);
        cz[2]=          (3.0*z2)-(6.0*z1)+(3.0*z0);
        cz[3]= (    z3)-(3.0*z2)+(3.0*z1)-(    z0);
        glBegin(GL_LINE_STRIP);
        for (t=0.0,j=0;j<20;j++,t+=0.05)
            {
            tt=t*t; ttt=tt*t;
            x=cx[0]+cx[1]*t+cx[2]*tt+cx[3]*ttt;
            y=cy[0]+cy[1]*t+cy[2]*tt+cy[3]*ttt;
            z=cz[0]+cz[1]*t+cz[2]*tt+cz[3]*ttt;
            glVertex3d(x,y,z);
            }
        glEnd();
        }
    glLineWidth(1);

    glFlush();
    SwapBuffers(hdc);
    }
//---------------------------------------------------------------------------

También utilicé la plantilla de lista dinámica mía, así que:


List<double> xxx; es lo mismo que double xxx[];
xxx.add(5); agrega 5 al final de la lista
xxx[7] elemento de matriz de acceso (seguro)
Elemento de matriz de acceso xxx.dat[7] (acceso directo inseguro pero rápido)
xxx.num es el tamaño real utilizado de la matriz
xxx.reset() borra la matriz y establece xxx.num=0
xxx.allocate(100) preasigna espacio para 100 elementos

Solo para estar segura de que el código es comprensible.

Y vista previa:

preview

Cuando desee editar su ruta, es mejor controlar los puntos de control cúbicos de interpolación en lugar de los bezier, ya que aprendió de la manera difícil que no son tan intuitivos y fáciles de manipular para lograr la salida deseada.

0
Spektre 7 may. 2020 a las 13:45