根据管端法线向圆柱体应用旋转
我正在尝试在 three.js 中制作一个弯曲的 3D 箭头.为了完成这项任务,我创建了一个
我正在尝试将箭头(圆柱形状为锥形)定位在管的末端,如下所示:(Photoshopped)
我的数学不是特别强,而且对 three.js 很陌生.有人可以帮助我了解如何将两者联系起来吗?
这是我当前的代码:
import T from 'three';var findY = 函数(r,x){返回 Math.sqrt((r * r) - (x * x));}变量半径 = 25;变量 x = 0;变量 z = 0;var numberOfPoints = 10;var 间隔 = (半径/numberOfPoints);变量点 = [];for (var i = numberOfPoints; i >= 0; i--){var y = findY(半径, x);points.push(new T.Vector3(x, y, z))x = x + 区间;}x = x - 间隔;for (var i = numberOfPoints - 1 ; i >= 0; i--){y = findY(半径, x) * -1;points.push(new T.Vector3(x, y, z));x = x - 间隔;}var path = new T.CatmullRomCurve3(points);var tubeGeometry = new T.TubeGeometry(路径,//路径10,//段radius/10,//半径8,//radiusSegmentsfalse//关闭);var coneGeometry = new T.CylinderGeometry(半径顶部 = 0.1,radiusBottom = 半径/5,高度 = 10,径向段 = 10,高度段 = 10,开放式 = 1);var 材料 = 新 T.MeshBasicMaterial( { 颜色: 0x00ff00 } );var tube = new T.Mesh(tubeGeometry, material);var cone = new T.Mesh(coneGeometry, material);//平移和旋转圆锥?
如果有人可以尝试简单地解释一下数学和编程完成的必要条件,我将不胜感激
- 找到位于管末端的法线
- 将圆锥体移动到正确的位置
感谢任何帮助!
解决方案当您可以直接在原地创建箭头时,不要为此使用旋转.同样,弯管也可以这样做.您唯一需要的是由 A,B
端点定义的最后一条线段.
令A
为尖点,B
为圆盘基中心.要创建箭头,您需要 2 个额外的基础向量,让它们称为基础圆盘的 U,V
和半径 r
.从它们中,您可以使用如下简单的圆形公式创建圆盘点:
获取
AB
端点计算
U,V
基向量U,V
应位于箭头的圆盘底部,且相互垂直.箭头的方向(行|BA|
)是圆盘基法线,因此利用叉积将垂直向量返回到相乘的向量,因此:W = B-A;W/= |W|;//单位向量T = (1,0,0);//temp 任何不平行于 W 的非零向量如果 ( |(W.T)|>0.75 ) T = (0,1,0);//如果 T 和 W 的绝对点积接近 1,则表示它们接近平行,因此选择不同的 TU = (T x W)//U 垂直于 T,WV = (U x W)//V 垂直于 U,W
创建/渲染箭头几何
即简单摊位
A,B
是三角扇的中心(需要2个),圆盘基点计算如下:P(ang) = B + U.r.cos(ang) + V.r.sin(ang)
因此,只需将
ang
循环遍历整个圆圈,然后您就可以获得足够的点数(通常 36 点就足够了)并从它们中进行两个三角形扇形.不要忘记最后一个圆盘点必须与第一个圆盘点相同,否则您会在ang = 0
或360
度数上出现丑陋的外观或孔.
如果您仍想进行轮换,则可以这样做.以与上述相同的方式计算 U,V,W
并从中构造变换矩阵.原点 O
将是点 B
轴 X,Y,Z
将是 U,V,W
顺序取决于您的箭头型号.W
应该与模型轴匹配.U,V
可以是任意顺序.所以只需将所有向量复制到它们的位置并使用此矩阵进行渲染.欲了解更多信息,请参阅:
void glArrowRoundxy(GLfloat x0,GLfloat y0,GLfloat z0,GLfloat r,GLfloat r0,GLfloat r1,GLfloat a0,GLfloat a1,GLfloat a2){常量 int _glCircleN=50;//每圈点数常量 int n=3*_glCircleN;整数 i,j,ix,e;浮动 x,y,z,x1,y1,z1,a,b,da,db=pi2/(_glCircleN-1);浮动 ux,uy,uz,vx,vy,vz,u,v;//缓冲区GLfloat ptab[6*_glCircleN],*p0,*p1,*n0,*n1,*p;p0=ptab+(0*_glCircleN);//上一个管段圆点p1=ptab+(3*_glCircleN);//实际管段圆点大=+分贝;如果(a0>a1)da=-db;//主角度步长方向ux=0.0;//U 垂直于箭头平面uy=0.0;uz=1.0;//圆弧插值 a=<a0,a1>对于 (e=1,j=0,a=a0;e;j++,a+=da){//结束条件如果 ((da>0.0)&&(a>=a1)) { a=a1;e=0;}if ((da<0.0)&&(a<=a1)) { a=a1;e=0;}//计算实际的管中心x1=x0+(r*cos(a));y1=y0+(r*sin(a));z1=z0;//V 是从 (x0,y0,z0) 到 (x1,y1,z1) 的方向vx=x1-x0;vy=y1-y0;vz=z1-z0;//和粗略的单位b=sqrt((vx*vx)+(vy*vy)+(vz*vz));如果(b>1e-6)b=1.0/b;否则 b=0.0;vx*=b;vy*=b;vz*=b;//管段对于 (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);九++;p1[ix]=y1+(uy*u)+(vy*v);九++;p1[ix]=z1+(uz*u)+(vz*v);九++;}如果 (!j){glBegin(GL_TRIANGLE_FAN);glVertex3f(x1,y1,z1);对于 (ix=0;ix
这会在 XY 平面上渲染弯曲的箭头,其中心为
x,y,z
,半径为r
.r0
是管半径,r1
是箭头基部半径.因为我没有你的曲线定义,所以我选择 XY 平面中的圆.a0,a1,a2
是箭头开始 (a0
)、箭头开始 (a1
) 和结束 (a2代码>).
pi2
只是常量pi2=6.283185307179586476925286766559
.这个想法是记住实际和之前的管段圆点,以便
ptab,p0,p1
存在,否则您需要计算所有内容两次.当我直接选择 XY 平面时,我知道一个基向量是垂直于它的.第二个是垂直于它和箭头方向幸运的是圆形属性提供了它自己,因此在这种情况下不需要叉积.
如果不评论我,希望它足够清楚.
我需要将它添加到我的引擎中,所以这里是 3D 版本(不仅绑定到轴对齐的箭头,而且圆锥体也弯曲了).除了基础向量计算之外它是相同的,我还在标题中稍微改变了角度
<a0,a1>
是整个间隔,aa
是箭头大小但后者在代码中被转换为原始约定.我还添加了用于照明计算的法线.我还添加了线性箭头,其中基向量的计算没有利用圆属性,以防你得到不同的曲线.这里的结果://---------------------------------------------------------------------------常量 int _glCircleN=50;//每圈点数//--------------------------------------------------------------------------void glCircleArrowxy(GLfloat x0,GLfloat y0,GLfloat z0,GLfloat r,GLfloat r0,GLfloat r1,GLfloat a0,GLfloat a1,GLfloat aa){双 pos[3]={ x0, y0, z0};双倍nor[3]={0.0,0.0,1.0};双 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){双 pos[3]={ x0, y0, z0};双nor[3]={1.0,0.0,0.0};双仓[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){双 pos[3]={ x0, y0, z0};双倍nor[3]={0.0,1.0,0.0};双仓[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){//常量 int _glCircleN=20;//每圈点数整数 e,i,j,N=3*_glCircleN;双 U[3],V[3],u,v;双a,b,da,db=pi2/double(_glCircleN-1),a2,rr;双 *ptab,*p0,*p1,*n0,*n1,*pp,p[3],q[3],c[3],n[3],tan[3];//缓冲区ptab=新双 [12*_glCircleN];如果 (ptab==NULL) 返回;p0=ptab+(0*_glCircleN);n0=ptab+(3*_glCircleN);p1=ptab+(6*_glCircleN);n1=ptab+(9*_glCircleN);//准备角度a2=a1;大=分贝;aa=晶圆厂(aa);如果(a0>a1){da=-da;aa=-aa;}a1-=aa;//计算缺失的基向量vector_copy(U,nor);//U 垂直于箭头平面vector_mul(tan,nor,bin);//切线垂直于法线和副法线//圆弧插值 a=<a0,a2>对于 (e=0,j=0,a=a0;e<5;j++,a+=da){//结束条件if (e==0)//e=0{如果 ((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如果 ((da>0.0)&&(a>=a2)) { a=a2;e++;}if ((da<0.0)&&(a<=a2)) { a=a2;e++;}rr=r1*fabs(除法(a-a2,a2-a1));}//计算实际管段中心 c[3]u=r*cos(a);v=r*sin(a);vector_mul(p,bin,u);矢量_mul(q,tan,v);vector_add(c,p,q);vector_add(c,c,pos);//V是从箭头中心到管段中心的单位方向vector_sub(V,c,pos);矢量_一个(V,V);//管段插值对于 (b=0.0,i=0;i
1)//重新计算圆锥的法线{对于 (i=3;i 0.0) for (i=0;i =0;i-=3) glVertex3dv(p1+i);else for (i= 0;i r1 边缘如果 (e==1) a-=da;如果 ((e==1)||(e==2)||(e==3)) e++;}//释放缓冲区删除[] ptab;}//--------------------------------------------------------------------------void glLinearArrow3D(double *pos,double *dir,double r0,double r1,double l,double al){//常量 int _glCircleN=20;//每圈点数int e,i,N=3*_glCircleN;双 U[3],V[3],W[3],u,v;双a,da=pi2/double(_glCircleN-1),r,t;双 *ptab,*p0,*p1,*n1,*pp,p[3],q[3],c[3],n[3];//缓冲区ptab=新双 [9*_glCircleN];如果 (ptab==NULL) 返回;p0=ptab+(0*_glCircleN);p1=ptab+(3*_glCircleN);n1=ptab+(6*_glCircleN);//计算基向量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));如果 (t>a) { pp=q;t=a;}a=fabs(vector_mul(W,n));如果 (t>a) { pp=n;t=a;}vector_mul(U,W,pp);vector_mul(V,U,W);vector_mul(U,V,W);对于 (e=0;e<4;e++){//分段中心如果 (e==0) { t=0.0;r=r0;}如果 (e==1) { t=l-al;r=r0;}如果 (e==2) { t=l-al;r=r1;}如果 (e==3) { t=l;r=0.0;}vector_mul(c,W,t);vector_add(c,c,pos);//管段插值对于 (a=0.0,i=0;i 2)//重新计算圆锥的法线{对于 (i=3;i 用法:
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);
和箭头的概述(在图像的右侧):
我正在使用我的矢量库,所以这里有一些解释:
vector_mul(a[3],b[3],c[3])
是叉积a = b x c
vector_mul(a[3],b[3],c)
是简单的标量乘法a = b.c
a = vector_mul(b[3],c[3])
是点积a = (b.c)
vector_one(a[3],b[3])
是单位向量a = b/|b|
vector_copy(a[3],b[3])
只是复制a = b
vector_add(a[3],b[3],c[3])
正在添加a = b + c
vector_sub(a[3],b[3],c[3])
正在减去a = b - c
vector_neg(a[3],b[3])
是否定a = -b
vector_ld(a[3],x,y,z)
正在加载a = (x,y,z)
pos
是圆箭头的中心位置,nor
是箭头所在平面的法线.bin
是双法线,角度从这个轴开始.应该垂直于nor
.r,r0,r1
是箭头的半径(弯、管、锥)线性箭头类似于
dir
是箭头的方向,l
是箭头大小,al
是箭头大小.I am attempting to make a curved 3D arrow in three.js. To accomplish this task, I have created a Tube that follows a curved path and a Cylinder shaped as a cone (by setting radiusTop to be tiny). They currently look like so:
I am attempting to position the Arrow Head (Cylinder shaped as a cone) at the end of the Tube like so: (Photoshopped)
I am not terribly strong in math and pretty new to three.js. Could someone help me understand how to connect the two?
Here is my current code:
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?
I would greatly appreciate if someone could attempt a simple explanation of what is necessary mathematically and programmatically accomplish
- Finding the normal located at the end of the tube
- Shifting the Cone to the correct location
Any help is appreciated!
解决方案Do not use rotation for this when you can create the arrowhead directly in place. Similarly the bended tube can be done this way too. Only thing you need for it is the last line segment defined by
A,B
endpoints.Let
A
be the sharp point andB
the disc base center. To create arrowhead you need 2 additional basis vectors let call themU,V
and radiusr
of base disc. From them you can create disc points with simple circle formula like this:obtain
AB
endpointscompute
U,V
basis vectorsThe
U,V
should lie in the disc base of arrowhead and should be perpendicular to each other. direction of the arrowhead (line|BA|
) is the disc base normal so exploit cross product which returns perpendicular vector to the multiplied ones so: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
create/render arrowhead geometry
That is easy booth
A,B
are centers of triangle fan (need 2) and the disc base points are computed like this:P(ang) = B + U.r.cos(ang) + V.r.sin(ang)
So just loop
ang
through the whole circle with some step so you got enough points (usually 36 is enough) and do both triangle fans from them. Do not forget the last disc point must be the same as the first one otherwise you will got ugly seems or hole on theang = 0
or360
deg.
If you still want to go for rotations instead then this is doable like this. compute
U,V,W
in the same way as above and construct transformation matrix from them. the originO
will be pointB
and axisesX,Y,Z
will beU,V,W
the order depends on your arrowhead model.W
should match the model axis.U,V
can be in any order. So just copy all the vectors to their places and use this matrix for rendering. For more info see:- Understanding 4x4 homogenous transform matrices
[Notes]
If you do not know how to compute vector operations like cross/dot products or absolute value see:
// 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))
[Edit1] simple GL implementation
I do not code in your environment but as downvote and comment suggest you guys are not able to put this together on your own which is odd considering you got this far so here simple C++/GL exmaple of how to do this (you can port this to your environment):
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(); }
This renders bended arrow in XY plane with center
x,y,z
and big radiusr
. Ther0
is tube radius andr1
is arrowhead base radius. As I do not have your curve definition I choose circle in XY plane. Thea0,a1,a2
are angles where arrow starts (a0
), arrowhead starts (a1
) and ends (a2
). Thepi2
is just constantpi2=6.283185307179586476925286766559
.The idea is to remember actual and previous tube segment circle points so there for the
ptab,p0,p1
otherwise you would need to compute everything twice.As I chose XY plane directly then I know that one base vector is normal to it. and second is perpendicular to it and to arrow direction luckily circle properties provides this on its own therefore no need for cross products in this case.
Hope it is clear enough if not comment me.
[Edit2]
I needed to add this to my engine so here is the 3D version (not bound just to axis aligned arrows and the cone is bended too). It is the same except the basis vector computation and I also change the angles a bit in the header
<a0,a1>
is the whole interval andaa
is the arrowhead size but latter in code it is converted to the original convention. I added also normals for lighting computations. I added also linear Arrow where the computation of basis vectors is not taking advantage of circle properties in case you got different curve. Here result://--------------------------------------------------------------------------- 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; } //---------------------------------------------------------------------------
usage:
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);
and overview of the arows (on the right side of image):
I am using my vector lib so here are some explanations:
vector_mul(a[3],b[3],c[3])
is cross producta = b x c
vector_mul(a[3],b[3],c)
is simple multiplication by scalara = b.c
a = vector_mul(b[3],c[3])
is dot producta = (b.c)
vector_one(a[3],b[3])
is unit vectora = b/|b|
vector_copy(a[3],b[3])
is just copya = b
vector_add(a[3],b[3],c[3])
is addinga = b + c
vector_sub(a[3],b[3],c[3])
is substractinga = b - c
vector_neg(a[3],b[3])
is negationa = -b
vector_ld(a[3],x,y,z)
is just loadinga = (x,y,z)
The
pos
is the center position of your circle arrow andnor
is normal of the plane in which the arrow lies.bin
is bi-normal and the angles are starting from this axis. should be perpendicular tonor
.r,r0,r1
are the radiuses of the arrow (bend,tube,cone)The linear arrow is similar the
dir
is direction of the arrow,l
is arrow size andal
is arrowhead size.
相关文章