2016-09-23 5 views
5

three.jsで曲線の3D矢印を作成しようとしています。この作業を達成するために、曲線パスに続いてを円錐として(radiusTopを小さく設定することによって)Tubeを作成しました。彼らは現在、そのようになります。(フォトショップ)チューブ終了に基づいてシリンダーに回転を適用する通常

2D

3D

は、私のようなので、管の端部に(シリンダーが円錐のような形状)アローヘッドを配置しようとしています

arrow mockup

私は数学で非常に強くないし、three.jsにはかなり新しいです。誰かが2つの接続方法を理解できるように助けてくれますか?ここで

は私の現在のコードです:

 import T from 'three'; 

     var findY = function(r, x) 
     { 
      return Math.sqrt((r * r) - (x * x)); 
     } 

     var radius = 25; 
     var x = 0; 
     var z = 0; 
     var numberOfPoints = 10; 
     var interval = (radius/numberOfPoints); 
     var points = []; 

     for (var i = numberOfPoints; i >= 0; i--) 
     { 
      var y = findY(radius, x); 
      points.push(new T.Vector3(x, y, z)) 
      x = x + interval; 
     } 

     x = x - interval; 

     for (var i = numberOfPoints - 1 ; i >= 0; i--) 
     { 
      y = findY(radius, x) * -1; 
      points.push(new T.Vector3(x, y, z)); 
      x = x - interval; 
     } 

     var path = new T.CatmullRomCurve3(points); 

     var tubeGeometry = new T.TubeGeometry(
      path, //path 
      10, //segments 
      radius/10,  //radius 
      8,  //radiusSegments 
      false //closed 
     ); 

     var coneGeometry = new T.CylinderGeometry(
      radiusTop = 0.1, 
      radiusBottom = radius/5, 
      height = 10, 
      radialSegments = 10, 
      heightSegments = 10, 
      openEnded = 1 
     ); 

     var material = new T.MeshBasicMaterial({ color: 0x00ff00 }); 

     var tube = new T.Mesh(tubeGeometry, material); 
     var cone = new T.Mesh(coneGeometry, material); 

     // Translate and Rotate cone? 

誰かが数学とプログラムでの端部に位置する通常の検索

  • を達成するには何が必要かを簡単に説明を試みることができれば、私は非常に感謝しますチューブ
  • 正しい位置に円錐を移動する

ご迷惑をおかけして申し訳ございません。

+0

編集1,2私の答え – Spektre

答えて

1

矢頭を直接作成できる場合は、回転を使用しないでください。同様に、屈曲管もこのようにすることができる。必要なものは、A,Bエンドポイントによって定義された最後の線分です。

Aをシャープポイントとし、Bをディスクベースセンターとします。矢頭を作成するには、基底ベクトルの2つの追加基底ベクトルが必要ですU,Vと半径を呼び出すことができます。彼らからは、このような単純な円形式でディスクポイントを作成することができます

arrow head

  1. ABエンドポイント

  2. コンピュートあるべきU,V基底ベクトル

    U,Vを取得します矢頭の円盤の中で、パーペお互いに関係しています。矢印(ライン|BA|)の方向は、通常、ディスクベースであるので、これ乗算したものに垂直なベクトルを返す外積悪用:

    W = B-A; 
    W /= |W|; // unit vector 
    T = (1,0,0); // temp any non zero vector not parallel to W 
    if (|(W.T)|>0.75) T = (0,1,0); // if abs dot product of T and W is close to 1 it means they are close to parallel so chose different T 
    U = (T x W) // U is perpendicular to T,W 
    V = (U x W) // V is perpendicular to U,W 
    
  3. を容易ブースある矢印幾何

    レンダリング/作成A,Bは、三角形ファン(必要性2)の中心であり、ディスクベースポイントは次のように計算される:

    P(ang) = B + U.r.cos(ang) + V.r.sin(ang) 
    

    だからループang全体の円からいくつかのステップであなたは十分なポイント(通常は36で十分です)を得て、それらから両方の三角ファンを行います。最後のディスクポイントが最初のディスクポイントと同じでなければならないことを忘れないでください。さもなければ、あなたは醜いと思われるか、ang = 0または360 degの穴に見えます。

代わりにローテーションをしたい場合は、このようにすることができます。上記と同様にしてU,V,Wを計算し、それらから変換行列を構築する。起点OB、軸はX,Y,ZU,V,Wとなります。順序は矢印のモデルによって異なります。 Wはモデル軸と一致する必要があります。 U,Vの順序は任意です。だから、すべてのベクトルをそれらの場所にコピーして、この行列をレンダリングに使用してください。詳細情報については以下を参照してください

[注]

をご覧のベクトルクロス/ドットの製品のような操作または絶対値を計算する方法がわからない場合は、次の

// cross product: W = U x V 
W.x=(U.y*V.z)-(U.z*V.y) 
W.y=(U.z*V.x)-(U.x*V.z) 
W.z=(U.x*V.y)-(U.y*V.x) 
// dot product: a = (U.V) 
a=U.x*V.x+U.y*V.y+U.z*V.z 
// abs of vector a = |U| 
a=sqrt((U.x*U.x)+(U.y*U.y)+(U.z*U.z)) 

[編集1]単純GL実装

downvoteとコメントがあなたたちは、あなたがどのようにこのはるかので、ここで簡単なC++/GL exmapleを持っ考慮奇数である自分で一緒にこれを置くことができない提案として、私はあなたの環境でコードしないが、この(ご使用の環境にこのことができますポート)を実行します。

overview

void glArrowRoundxy(GLfloat x0,GLfloat y0,GLfloat z0,GLfloat r,GLfloat r0,GLfloat r1,GLfloat a0,GLfloat a1,GLfloat a2) 
    { 
    const int _glCircleN=50; // points per circle 
    const int n=3*_glCircleN; 
    int i,j,ix,e; 
    float x,y,z,x1,y1,z1,a,b,da,db=pi2/(_glCircleN-1); 
    float ux,uy,uz,vx,vy,vz,u,v; 
    // buffers 
    GLfloat ptab[6*_glCircleN],*p0,*p1,*n0,*n1,*p; 
    p0=ptab+(0*_glCircleN);  // previous tube segment circle points 
    p1=ptab+(3*_glCircleN);  // actual tube segment circle points 
    da=+db; if (a0>a1) da=-db; // main angle step direction 
    ux=0.0;      // U is normal to arrow plane 
    uy=0.0; 
    uz=1.0; 
    // arc interpolation a=<a0,a1> 
    for (e=1,j=0,a=a0;e;j++,a+=da) 
     { 
     // end conditions 
     if ((da>0.0)&&(a>=a1)) { a=a1; e=0; } 
     if ((da<0.0)&&(a<=a1)) { a=a1; e=0; } 
     // compute actual tube ceneter 
     x1=x0+(r*cos(a)); 
     y1=y0+(r*sin(a)); 
     z1=z0; 
     // V is direction from (x0,y0,z0) to (x1,y1,z1) 
     vx=x1-x0; 
     vy=y1-y0; 
     vz=z1-z0; 
     // and unit of coarse 
     b=sqrt((vx*vx)+(vy*vy)+(vz*vz)); 
     if (b>1e-6) b=1.0/b; else b=0.0; 
     vx*=b; 
     vy*=b; 
     vz*=b; 
     // tube segment 
     for (ix=0,b=0.0,i=0;i<_glCircleN;i++,b+=db) 
      { 
      u=r0*cos(b); 
      v=r0*sin(b); 
      p1[ix]=x1+(ux*u)+(vx*v); ix++; 
      p1[ix]=y1+(uy*u)+(vy*v); ix++; 
      p1[ix]=z1+(uz*u)+(vz*v); ix++; 
      } 
     if (!j) 
      { 
      glBegin(GL_TRIANGLE_FAN); 
      glVertex3f(x1,y1,z1); 
      for (ix=0;ix<n;ix+=3) glVertex3fv(p1+ix); 
      glEnd(); 
      } 
     else{ 
      glBegin(GL_QUAD_STRIP); 
      for (ix=0;ix<n;ix+=3) 
       { 
       glVertex3fv(p0+ix); 
       glVertex3fv(p1+ix); 
       } 
      glEnd(); 
      } 
     // swap buffers 
     p=p0; p0=p1; p1=p; 
     p=n0; n0=n1; n1=p; 
     } 
    // arrowhead a=<a1,a2> 
    for (ix=0,b=0.0,i=0;i<_glCircleN;i++,b+=db) 
     { 
     u=r1*cos(b); 
     v=r1*sin(b); 
     p1[ix]=x1+(ux*u)+(vx*v); ix++; 
     p1[ix]=y1+(uy*u)+(vy*v); ix++; 
     p1[ix]=z1+(uz*u)+(vz*v); ix++; 
     } 
    glBegin(GL_TRIANGLE_FAN); 
    glVertex3f(x1,y1,z1); 
    for (ix=0;ix<n;ix+=3) glVertex3fv(p1+ix); 
    glEnd(); 
    x1=x0+(r*cos(a2)); 
    y1=y0+(r*sin(a2)); 
    z1=z0; 
    glBegin(GL_TRIANGLE_FAN); 
    glVertex3f(x1,y1,z1); 
    for (ix=n-3;ix>=0;ix-=3) glVertex3fv(p1+ix); 
    glEnd(); 
    } 

これは、中心x,y,zと大きな半径rとXY平面に曲がった矢印を描画します。 r0は管の半径であり、r1は矢頭の底半径です。あなたの曲線定義がないので、私はXY平面内で円を選択します。 a0,a1,a2は、矢印が始まり(a0)、矢頭が始まり(a1)、終わり(a2)の角度です。 pi2はちょうど一定であるpi2=6.283185307179586476925286766559です。

実際のチューブセグメントの円点を覚えておいてください。ptab,p0,p1の場合は、すべてを2回計算する必要があります。

私はXY平面を直接選択したので、私は1つの基底ベクトルがそれに垂直であることを知っています。2番目はそれに垂直であり、矢印の方向には幸運にも円の性質があるため、それ自体でこれを提供します。したがって、この場合、クロスプロダクトは必要ありません。

コメントしていないと分かりますか?

[EDIT2]

私はここで私のエンジンにこれを追加するために必要な3Dバージョン(ちょうど整列矢を軸にバインドされ、コーンはあまり曲げられていない)です。それは基底ベクトルの計算を除いて同じであり、私はまた、<a0,a1>が全体の間隔であり、aaが矢じりサイズであるが、後者のコードでは元の規則に変換された角度を変更する。ライティング計算の法線も追加しました。私は、あなたが別の曲線を持っている場合に、基底ベクトルの計算が円のプロパティを利用していない線形の矢印を追加しました。ここで結果:

//--------------------------------------------------------------------------- 
const int _glCircleN=50; // points per circle 
//--------------------------------------------------------------------------- 
void glCircleArrowxy(GLfloat x0,GLfloat y0,GLfloat z0,GLfloat r,GLfloat r0,GLfloat r1,GLfloat a0,GLfloat a1,GLfloat aa) 
    { 
    double pos[3]={ x0, y0, z0}; 
    double nor[3]={0.0,0.0,1.0}; 
    double bin[3]={1.0,0.0,0.0}; 
    glCircleArrow3D(pos,nor,bin,r,r0,r1,a0,a1,aa); 
    } 
//--------------------------------------------------------------------------- 
void glCircleArrowyz(GLfloat x0,GLfloat y0,GLfloat z0,GLfloat r,GLfloat r0,GLfloat r1,GLfloat a0,GLfloat a1,GLfloat aa) 
    { 
    double pos[3]={ x0, y0, z0}; 
    double nor[3]={1.0,0.0,0.0}; 
    double bin[3]={0.0,1.0,0.0}; 
    glCircleArrow3D(pos,nor,bin,r,r0,r1,a0,a1,aa); 
    } 
//--------------------------------------------------------------------------- 
void glCircleArrowxz(GLfloat x0,GLfloat y0,GLfloat z0,GLfloat r,GLfloat r0,GLfloat r1,GLfloat a0,GLfloat a1,GLfloat aa) 
    { 
    double pos[3]={ x0, y0, z0}; 
    double nor[3]={0.0,1.0,0.0}; 
    double bin[3]={0.0,0.0,1.0}; 
    glCircleArrow3D(pos,nor,bin,r,r0,r1,a0,a1,aa); 
    } 
//--------------------------------------------------------------------------- 
void glCircleArrow3D(double *pos,double *nor,double *bin,double r,double r0,double r1,double a0,double a1,double aa) 
    { 
// const int _glCircleN=20; // points per circle 
    int e,i,j,N=3*_glCircleN; 
    double U[3],V[3],u,v; 
    double a,b,da,db=pi2/double(_glCircleN-1),a2,rr; 
    double *ptab,*p0,*p1,*n0,*n1,*pp,p[3],q[3],c[3],n[3],tan[3]; 
    // buffers 
    ptab=new double [12*_glCircleN]; if (ptab==NULL) return; 
    p0=ptab+(0*_glCircleN); 
    n0=ptab+(3*_glCircleN); 
    p1=ptab+(6*_glCircleN); 
    n1=ptab+(9*_glCircleN); 
    // prepare angles 
    a2=a1; da=db; aa=fabs(aa); 
    if (a0>a1) { da=-da; aa=-aa; } 
    a1-=aa; 
    // compute missing basis vectors 
    vector_copy(U,nor);   // U is normal to arrow plane 
    vector_mul(tan,nor,bin); // tangent is perpendicular to normal and binormal 
    // arc interpolation a=<a0,a2> 
    for (e=0,j=0,a=a0;e<5;j++,a+=da) 
     { 
     // end conditions 
     if (e==0) // e=0 
      { 
      if ((da>0.0)&&(a>=a1)) { a=a1; e++; } 
      if ((da<0.0)&&(a<=a1)) { a=a1; e++; } 
      rr=r0; 
      } 
     else{  // e=1,2,3,4 
      if ((da>0.0)&&(a>=a2)) { a=a2; e++; } 
      if ((da<0.0)&&(a<=a2)) { a=a2; e++; } 
      rr=r1*fabs(divide(a-a2,a2-a1)); 
      } 
     // compute actual tube segment center c[3] 
     u=r*cos(a); 
     v=r*sin(a); 
     vector_mul(p,bin,u); 
     vector_mul(q,tan,v); 
     vector_add(c,p, q); 
     vector_add(c,c,pos); 
     // V is unit direction from arrow center to tube segment center 
     vector_sub(V,c,pos); 
     vector_one(V,V); 
     // tube segment interpolation 
     for (b=0.0,i=0;i<N;i+=3,b+=db) 
      { 
      u=cos(b); 
      v=sin(b); 
      vector_mul(p,U,u);  // normal 
      vector_mul(q,V,v); 
      vector_add(n1+i,p,q); 
      vector_mul(p,n1+i,rr); // vertex 
      vector_add(p1+i,p,c); 
      } 
     if (e>1)     // recompute normals for cone 
      { 
      for (i=3;i<N;i+=3) 
       { 
       vector_sub(p,p0+i ,p1+i); 
       vector_sub(q,p1+i-3,p1+i); 
       vector_mul(p,p,q); 
       vector_one(n1+i,p); 
       } 
      vector_sub(p,p0 ,p1); 
      vector_sub(q,p1+N-3,p1); 
      vector_mul(p,q,p); 
      vector_one(n1,p); 
      if (da>0.0) for (i=0;i<N;i+=3) vector_neg(n1+i,n1+i); 
      if (e== 3) for (i=0;i<N;i+=3) vector_copy(n0+i,n1+i); 
      } 
     // render base disc 
     if (!j) 
      { 
      vector_mul(n,U,V); 
      glBegin(GL_TRIANGLE_FAN); 
      glNormal3dv(n); 
      glVertex3dv(c); 
      if (da<0.0) for (i=N-3;i>=0;i-=3) glVertex3dv(p1+i); 
      else  for (i= 0;i< N;i+=3) glVertex3dv(p1+i); 
      glEnd(); 
      } 
     // render tube 
     else{ 
      glBegin(GL_QUAD_STRIP); 
      if (da<0.0) for (i=0;i<N;i+=3) 
       { 
       glNormal3dv(n1+i); glVertex3dv(p1+i); 
       glNormal3dv(n0+i); glVertex3dv(p0+i); 
       } 
      else for (i=0;i<N;i+=3) 
       { 
       glNormal3dv(n0+i); glVertex3dv(p0+i); 
       glNormal3dv(n1+i); glVertex3dv(p1+i); 
       } 
      glEnd(); 
      } 
     // swap buffers 
     pp=p0; p0=p1; p1=pp; 
     pp=n0; n0=n1; n1=pp; 
     // handle r0 -> r1 edge 
     if (e==1) a-=da; 
     if ((e==1)||(e==2)||(e==3)) e++; 
     } 
    // release buffers 
    delete[] ptab; 
    } 
//--------------------------------------------------------------------------- 
void glLinearArrow3D(double *pos,double *dir,double r0,double r1,double l,double al) 
    { 
// const int _glCircleN=20; // points per circle 
    int e,i,N=3*_glCircleN; 
    double U[3],V[3],W[3],u,v; 
    double a,da=pi2/double(_glCircleN-1),r,t; 
    double *ptab,*p0,*p1,*n1,*pp,p[3],q[3],c[3],n[3]; 
    // buffers 
    ptab=new double [9*_glCircleN]; if (ptab==NULL) return; 
    p0=ptab+(0*_glCircleN); 
    p1=ptab+(3*_glCircleN); 
    n1=ptab+(6*_glCircleN); 
    // compute basis vectors 
    vector_one(W,dir); 
    vector_ld(p,1.0,0.0,0.0); 
    vector_ld(q,0.0,1.0,0.0); 
    vector_ld(n,0.0,0.0,1.0); 
    a=fabs(vector_mul(W,p));   pp=p; t=a; 
    a=fabs(vector_mul(W,q)); if (t>a) { pp=q; t=a; } 
    a=fabs(vector_mul(W,n)); if (t>a) { pp=n; t=a; } 
    vector_mul(U,W,pp); 
    vector_mul(V,U,W); 
    vector_mul(U,V,W); 
    for (e=0;e<4;e++) 
     { 
     // segment center 
     if (e==0) { t=0.0; r= r0; } 
     if (e==1) { t=l-al; r= r0; } 
     if (e==2) { t=l-al; r= r1; } 
     if (e==3) { t=l; r=0.0; } 
     vector_mul(c,W,t); 
     vector_add(c,c,pos); 
     // tube segment interpolation 
     for (a=0.0,i=0;i<N;i+=3,a+=da) 
      { 
      u=cos(a); 
      v=sin(a); 
      vector_mul(p,U,u);  // normal 
      vector_mul(q,V,v); 
      vector_add(n1+i,p,q); 
      vector_mul(p,n1+i,r); // vertex 
      vector_add(p1+i,p,c); 
      } 
     if (e>2)     // recompute normals for cone 
      { 
      for (i=3;i<N;i+=3) 
       { 
       vector_sub(p,p0+i ,p1+i); 
       vector_sub(q,p1+i-3,p1+i); 
       vector_mul(p,p,q); 
       vector_one(n1+i,p); 
       } 
      vector_sub(p,p0 ,p1); 
      vector_sub(q,p1+N-3,p1); 
      vector_mul(p,q,p); 
      vector_one(n1,p); 
      } 
     // render base disc 
     if (!e) 
      { 
      vector_neg(n,W); 
      glBegin(GL_TRIANGLE_FAN); 
      glNormal3dv(n); 
      glVertex3dv(c); 
      for (i=0;i<N;i+=3) glVertex3dv(p1+i); 
      glEnd(); 
      } 
     // render tube 
     else{ 
      glBegin(GL_QUAD_STRIP); 
      for (i=0;i<N;i+=3) 
       { 
       glNormal3dv(n1+i); 
       glVertex3dv(p0+i); 
       glVertex3dv(p1+i); 
       } 
      glEnd(); 
      } 
     // swap buffers 
     pp=p0; p0=p1; p1=pp; 
     } 
    // release buffers 
    delete[] ptab; 
    } 
//--------------------------------------------------------------------------- 

用法:

glColor3f(0.5,0.5,0.5); 

glCircleArrowyz(+3.5,0.0,0.0,0.5,0.1,0.2,0.0*deg,+270.0*deg,45.0*deg); 

glCircleArrowyz(-3.5,0.0,0.0,0.5,0.1,0.2,0.0*deg,-270.0*deg,45.0*deg); 
glCircleArrowxz(0.0,+3.5,0.0,0.5,0.1,0.2,0.0*deg,+270.0*deg,45.0*deg); 
glCircleArrowxz(0.0,-3.5,0.0,0.5,0.1,0.2,0.0*deg,-270.0*deg,45.0*deg); 
glCircleArrowxy(0.0,0.0,+3.5,0.5,0.1,0.2,0.0*deg,+270.0*deg,45.0*deg); 
glCircleArrowxy(0.0,0.0,-3.5,0.5,0.1,0.2,0.0*deg,-270.0*deg,45.0*deg); 
glColor3f(0.2,0.2,0.2); 
glLinearArrow3D(vector_ld(+2.0,0.0,0.0),vector_ld(+1.0,0.0,0.0),0.1,0.2,2.0,0.5); 
glLinearArrow3D(vector_ld(-2.0,0.0,0.0),vector_ld(-1.0,0.0,0.0),0.1,0.2,2.0,0.5); 
glLinearArrow3D(vector_ld(0.0,+2.0,0.0),vector_ld(0.0,+1.0,0.0),0.1,0.2,2.0,0.5); 
glLinearArrow3D(vector_ld(0.0,-2.0,0.0),vector_ld(0.0,-1.0,0.0),0.1,0.2,2.0,0.5); 
glLinearArrow3D(vector_ld(0.0,0.0,+2.0),vector_ld(0.0,0.0,+1.0),0.1,0.2,2.0,0.5); 
glLinearArrow3D(vector_ld(0.0,0.0,-2.0),vector_ld(0.0,0.0,-1.0),0.1,0.2,2.0,0.5); 

とarowsの概要(画像の右側):

arrows

を私はここに私のベクトルのlibを使用していますいくつかの説明があります:


vector_mul(a[3],b[3],c[3])a = b x c
vector_mul(a[3],b[3],c)a = b/|b|
vector_copy(a[3],b[3])ちょうどa = b
vector_add(a[3],b[3],c[3])
vector_sub(a[3],b[3],c[3])a = b + cを追加してコピーされたスカラーa = b.c
a = vector_mul(b[3],c[3])による単純な乗算は、ドット積a = (b.c)
vector_one(a[3],b[3])である単位ベクトルであるa = b - c
vector_neg(a[3],b[3])をsubstractingれるクロス生成物であります否定ですa = -b
vector_ld(a[3],x,y,z)がちょうどposa = (x,y,z)

をロードしていることはあなたのサークルの矢印の中心位置であるとnorは、平面内の矢印嘘の正常です。 binは双安定であり、角度はこの軸から始まります。 norに垂直である必要があります。 r,r0,r1が矢印の半径(曲げ、管、円錐)

あるリニア矢印はdir同様でl矢印サイズとalが矢印サイズであり、矢印の方向です。

+0

あなたの説明は非常に徹底していますが、これを実装する方法のプログラム的な説明が尋ねられました。例えば、three.jsを使って 'v'と' u'という単位ベクトルを見つけるにはどうすればよいですか? –

+0

@MattVS追加GLの例 – Spektre

関連する問題