Morph a cube to coil in three js
Asked Answered
S

1

3

I have tried to morph a thin rectangular cube to coil by three.js and tween.js. I've searched questions that already has been ask but none of them guide me.

Which morph function should I use, and how can I create a coil shape?

cube to coil

Sascha answered 27/1, 2016 at 9:1 Comment(3)
Can you provide a picture of what you want the result to look like?Bashemath
Added C++ OpenGL exampleExpound
Accidentally got back here and I see you got Cube->Coil instead of Tube->Coil which I obviously overlooked) And as this is still not accepted Do you really need the cube? (your image suggest tube) if yes the coil should have circular cut or square cut of the wire ?Expound
E
0
  1. have your tube stored as a set of N slices

    You need center point and normal vector for each (circle) slice.

    const int N=128;
    struct slice { float p[3],n[3]; };
    slice mesh[N];
    

    Init it to tube at start (aligned to y axis)

    for (int i=0;i<N;i++)
     {
     mesh[i].p[0]= 0.0;
     mesh[i].p[1]=-1.0+2.0*float(i)/float(N-1);
     mesh[i].p[2]= 0.0;
     mesh[i].n[0]= 0.0;
     mesh[i].n[1]=+1.0;
     mesh[i].n[2]= 0.0;
     }
    
  2. write visualization code for such mesh representation

    You need to obtain the circumference points of each slice and join them with QUAD_STRIP or what ever primitive you are using for the tube surface. The top and bottom is best with TRIANGLE_FAN around center point.

    You can obtain the points with my glCircle3D in C++ just instead of drawing them store/use them as you need ...

  3. interpolate between tube and helix

    If the above is workung you can turn the centers position and normals into helix with variable radius r and fixed screws m. So I would try:

    float a=6.283185307179586476925286766559*float(i*m)/float(N-1);
    mesh[i].p[0] = r*cos(a);
    mesh[i].p[2] = r*sin(a);
    

    The normal can be computed similary but I do not have the time for testing it right now and my imagination is not that good so i would instead do this:

    mesh[i].n[0] = mesh[i].p[0] - mesh[i-1].p[0];
    mesh[i].n[1] = mesh[i].p[1] - mesh[i-1].p[1]; 
    mesh[i].n[2] = mesh[i].p[2] - mesh[i-1].p[2];
    normalize(mesh[i].n); // set to unit vector
    

    Just copy the normal from slice 1 to slice 0 and you should be fine.

  4. animate

    Just animate the mesh with changing r from zero to some R. If you want continuous effect you can do r=R*sin(t) where t is increasing with some step ...

[edit1] C++ OpenGL example

If you put all the above together you should get something like this:

//---------------------------------------------------------------------------
//         height  ,tube r  ,screw r ,screws
void helix(double h,double r,double R,double N)
    {
    int i,j,na;
    double pos[3]={ 0.0,0.0,0.0 },x[3],y[3],
           nor[3]={ 0.0,1.0,0.0 },ss,dy,a,da,b,db;
    na=double(N*36.0);              // 36 slices per screw
    const int nb=36+1;              // 36 points per circle slice
    dy=h/double(na);                // y axis step
    da=2.0*M_PI*N/double(na);       // screw angle step
    db=2.0*M_PI/double(nb-1);       // slice circle angle step
    ss=1.0/sqrt((R*R)+(dy*dy));     // normalization scale
    double pnt[nb*12],*p0=pnt,*p1=pnt+(nb*6),*pp;   // 2 slice point buffers (normal3d+vertex3d)*nb*2 = 12*nb
    for (a=0.0,i=0;i<na;i++,a+=da)
        {
        if (a>2.0*M_PI) a-=2.0*M_PI;
        // slice center
        pos[0]=R*cos(a);
        pos[1]+=dy;
        pos[2]=R*sin(a);
        // slice normal
        nor[0]=-ss*R*sin(a);
        nor[1]=+ss*dy;
        nor[2]=+ss*R*cos(a);
        // slice basis vectors x,y
        x[0]=cos(a);
        x[1]=0.0;
        x[2]=sin(a);
        // y = cross(x,nor)
        y[0]=             -(x[2]*nor[1]);
        y[1]=(x[2]*nor[0])-(x[0]*nor[2]);
        y[2]=(x[0]*nor[1]);
        // get the slice points (remember 2 slices for QUAD STRIP) to actual point buffer p1
        for (pp=p1,b=0.0,j=0;j<nb;j++,b+=db,pp+=6)
            {
            // normal
            pp[0]=(x[0]*cos(b))+(y[0]*sin(b));
            pp[1]=(x[1]*cos(b))+(y[1]*sin(b));
            pp[2]=(x[2]*cos(b))+(y[2]*sin(b));
            // position
            pp[3]=pos[0]+(pp[0]*r);
            pp[4]=pos[1]+(pp[1]*r);
            pp[5]=pos[2]+(pp[2]*r);
            }
        // if 2 slices done render the slice between last slice p0 and actual slice p1
        glBegin(GL_QUAD_STRIP);
        if (i) for (j=0;j<6*nb;j+=6)
            {
            glNormal3dv(p0+j+0);
            glVertex3dv(p0+j+3);
            glNormal3dv(p1+j+0);
            glVertex3dv(p1+j+3);
            }
        glEnd();
        // swap last,actual slice point buffers p0 <-> p1
        pp=p0; p0=p1; p1=pp;
        }
    }
//---------------------------------------------------------------------------

Which renders helix in OpenGL. it starts from (0,0,0) and ends in (0,h,0)where:

  • r is the radius of the tube
  • R is the radius of the screws
  • h is helix height/size
  • N is number of screws per h

It generates Vertex and Normal info so you can use lighting. for animation I use this:

static double t=0.0; t+=0.1; if (t>=pi2) t-=pi2;
double R=sin(t); if (R<0.0) R=0.0;
glColor3f(1.0,1.0,1.0); helix(1.0,0.05,0.3*R,6.0);

As you can see half of the sin wave is neglected so you can have time to actually see the tube without the screws. Here output with lighting:

helix

On the left is the unscrewed tube (R=0). On the right is fully morphed screw and in the middle is something in between.

PS if you want to make the morph more interesting you can also animate the N parameter from 0 to some constant.

Expound answered 28/1, 2016 at 8:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.