做者:feiquan 出處:http://www.cnblogs.com/feiquan/ 版權聲明:本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。 你們寫文都不容易,請尊重勞動成果~ 這裏謝謝你們啦(*/ω\*)
首先,咱們須要存儲一個立方體的相關信息。html
建立類Point_3V來存放三維點,Line_2V來存放二維點,Line來存放三維線,Line_2V來存放二維線,face_2V來存放二維面,face來存放三維面,cube來定義一個矩形。以上的二維都是用來平行投影時使用的,三維則是存放在三維空間中真實存在的矩形的信息,且這幾個三維類之間使用了類的繼承來實現。編程
有關面的檢測,使用了背面檢測法。數組
有關面的填充,使用了種子填充。函數
代碼以下:測試
//3V_Point三維點 class Point_3V{ public: double x,y,z; bool v; Point_3V(){ x=0;y=0;z=0; v=false; } Point_3V(double a,double b,double c){ x=a;y=b;z=c; v=false; } Point_3V(double a,CPoint p){ x=a;y=p.x;z=p.y; v=false; } Point_3V(CPoint p,double a){ x=p.x;y=p.y;z=a; v=false; } void set_Point(double a,double b,double c){ x=a;y=b;z=c; } void set_Point(Point_3V p){ x=p.x;y=p.y;z=p.z; } void set_Point(Point_3V *p){ x=p->x;y=p->y;z=p->z; } //投影一個三維點,返回投影點 CPoint reflect_Point_3V(Point_3V v,CPoint move){ CPoint p2; double a,b; a=this->x-v.x/v.z*this->z+move.x; b=this->y-v.y/v.z*this->z+move.y; p2.SetPoint((int)a,(int)b); return p2; } }; //二維線 class Line_2V{ public : CPoint start,end; Line_2V(){ start.SetPoint(0,0); end.SetPoint(0,0); } Line_2V(CPoint x,CPoint y ){ start=x;end=y; } void fill(CDC *p){ p->MoveTo(start); p->LineTo(end); } }; //基於點填充線(不會開闢新空間) class Line :public Point_3V { public : Point_3V *start; Point_3V end; bool v; Line(){ start=new Point_3V[1]; v=false; } Line(int a,int b,int c ,Point_3V e ):Point_3V(a,b,c),start(this),end(e){ v=false; } Line( int s_x,int s_y,int s_z,int e_x,int e_y,int e_z):Point_3V(s_x,s_y,s_z),start(this),end(e_x,e_y,e_z){ v=false; } Line(Line *p){ this->start=p->start; this->end=p->end; v=false; } //三維線投影 Line_2V reflect_Line(Point_3V v,CPoint move,bool draw,CDC *p){ CPoint s=start->reflect_Point_3V(v,move); CPoint e=end.reflect_Point_3V(v,move); Line_2V temp(s,e); if(draw)temp.fill(p); return temp; } void set_Line(int s_x,int s_y,int s_z,int e_x,int e_y,int e_z){ this->start->set_Point(s_x,s_y,s_z); this->end.set_Point(e_x,e_y,e_z); } void set_Line(Point_3V s,Point_3V e){ this->x=s.x; this->y=s.y; this->z=s.z; this->v=s.v; this->start->set_Point(s); this->end.set_Point(e); } }; class face_2V{ public : //逆時針 Line_2V a,b,c,d; face_2V(){ } face_2V(Line_2V i,Line_2V j,Line_2V k,Line_2V l ){ a=i;b=j;c=k;d=l; } void B(int x,int y,int c1_fill,int c2,CDC *p){ //種子填充 int center=p->GetPixel(x,y); if(center!=c1_fill&¢er!=c2){ p->SetPixel(x,y,c1_fill); B(x+1,y,c1_fill,c2,p);B(x-1,y,c1_fill,c2,p); B(x,y+1,c1_fill,c2,p);B(x,y-1,c1_fill,c2,p); } } void fill(int c1_fill,int c2,CDC *p){ a.fill(p);b.fill(p);c.fill(p);d.fill(p); B(((a.start.x+c.start.x)/2),((a.start.y+c.start.y )/2), c1_fill, c2,p); } }; //基於點填充面(不會開闢新空間) class face :public Line{ public : Point_3V *p;//逆時針 Line *l1,l2,l3,l4;//l1只能是指針,爲了是其與點公用一個存儲空間 bool v; face(){ p=new Point_3V[4]; l1=new Line[1]; v=false; } face(Point_3V *q[4]){ this->start=q[0]; this->end=*q[1]; p=new Point_3V[4]; l1=new Line[1]; v=false; l1->set_Line(p[0],p[1]); l2.set_Line(p[1],p[2]); l3.set_Line(p[2],p[4]); l4.set_Line(p[4],p[0]); } face(Point_3V a,Point_3V b,Point_3V c,Point_3V d){ p=new Point_3V[4]; l1=new Line[1]; p[0]=a;p[0]=b,p[0]=c,p[0]=d; v=false; l1->set_Line(p[0],p[1]); l2.set_Line(p[1],p[2]); l3.set_Line(p[2],p[4]); l4.set_Line(p[4],p[0]); } face( face *p1){ p=new Point_3V[4]; l1=new Line[1]; this->start=p1->start; this->end=p1->end; p[0]=p1->p[0]; p[1]=p1->p[1]; l1->set_Line(p[0],p[1] ); v=false; } face(int s_x,int s_y,int s_z,int e_x,int e_y,int e_z,Line p2,Line p3,Line p4):Line(s_x,s_y,s_z,e_x,e_y,e_z),l1(this),l2(p2),l3(p3),l4(p4){ p=new Point_3V[4]; l1=new Line[1]; v=false; } void set_Point(Point_3V q[4]){ for(int i=0;i<4;i++){ p[i]=q[i]; } } void set_Line(){ l1->set_Line(p[0],p[1]); l2.set_Line(p[1],p[2]); l3.set_Line(p[2],p[4]); l4.set_Line(p[4],p[0]); } void set_Face(Point_3V q[4]){ for(int i=0;i<4;i++){ p[i]=q[i]; } l1->set_Line(p[0],p[1]); l2.set_Line(p[1],p[2]); l3.set_Line(p[2],p[4]); l4.set_Line(p[4],p[0]); } void set_Face(Point_3V q1,Point_3V q2,Point_3V q3,Point_3V q4){ p[0]=q1; p[1]=q2; p[2]=q3; p[3]=q4; l1->set_Line(p[0],p[1]); l2.set_Line(p[1],p[2]); l3.set_Line(p[2],p[3]); l4.set_Line(p[3],p[0]); } //三維向量的向量積 Point_3V xiangliangji( Point_3V a ,Point_3V b){ //矩陣形式,和i,j,k是否爲偶數或奇數有關,切記 return Point_3V(a.y*b.z-a.z*b.y,-(a.x*b.z-a.z*b.x),a.x*b.y-a.y*b.x); } //三維向量的點乘 double diancheng( Point_3V a ,Point_3V b){ double temp=a.x*b.x+a.y*b.y+a.z*b.z; return temp; } //求一個面的法向量,輸入一個面按逆時針方向的全部點的數組 Point_3V n( face *one_face){ Point_3V a,b,n; if(one_face->p!=NULL){ a.set_Point(one_face->p[1].x-one_face->p[0].x,one_face->p[1].y-one_face->p[0].y,one_face->p[1].z-one_face->p[0].z); b.set_Point(one_face->p[2].x-one_face->p[0].x,one_face->p[2].y-one_face->p[0].y,one_face->p[2].z-one_face->p[0].z); n=xiangliangji(a,b); return n; }else{ return n; } } //判斷一個面是否可見,若是一個面可見,則這個面上的四個點也可見 bool view_face(face *one_face, Point_3V v){ double cos,a_mo,b_mo; //求一個面的法向量 Point_3V fa; fa=n(one_face); double a_temp=pow((double)fa.x,2)+pow((double)fa.y,2)+pow((double)fa.z,2); a_mo=sqrt(a_temp); double b_temp=pow(v.x,2)+pow(v.y,2)+pow(v.z,2); b_mo=sqrt(b_temp); double fz=diancheng(fa,v); double fm=a_mo*b_mo; cos=fz/fm; if(cos<=0){ one_face->v=true; //判斷這個多邊形體的各個點是否可見 for(int j=0;j<4;j++){ one_face->p[j].v=true; } return true; }else{ return false; } } //3V面投影 void reflect_Face(Point_3V v,CPoint move,bool draw_Line,bool fill_face,int c1_fill,int c2,CDC *p){ if(view_face(this,v)){ Line_2V l2_1=l1->reflect_Line(v,move,draw_Line,p); Line_2V l2_2=l2.reflect_Line(v,move,draw_Line,p); Line_2V l2_3=l3.reflect_Line(v,move,draw_Line,p); Line_2V l2_4=l4.reflect_Line(v,move,draw_Line,p); if(fill_face){ face_2V f2(l2_1,l2_2,l2_3,l2_4); f2.fill(c1_fill,c2,p); } } } }; //多邊形 p+f-l=2 class cube{ private: bool isCube; public : int point_num,face_num,line_num; Point_3V p[8]; Line l[12]; face f[6]; cube(){ point_num=0; face_num=0; line_num=0; } cube(int point_nums,int line_nums,int face_nums){ if(point_nums+face_nums-line_nums==2){//公式 point_num=point_nums; face_num=face_nums; line_num=line_nums; /*p=new Point_3V[point_num]; l=new Line[line_num]; f=new face[face_num];*/ isCube=true; }else{ cube(); isCube=false; } } void set_Point(Point_3V *point){ for(int i=0;i<point_num;i++){ p[i]=point[i]; } } void set_cube(Point_3V *point){ set_Point(point); //上下 左右 先後 f[0].set_Face(p[0],p[1],p[2],p[3]);//上 f[1].set_Face( p[7],p[6],p[5],p[4]);//下 f[2].set_Face(p[0],p[4],p[5],p[1]);//左 f[3].set_Face(p[3],p[2],p[6],p[7]);//右 f[4].set_Face(p[1],p[5],p[6],p[2]);//前 f[5].set_Face(p[0],p[3],p[7],p[4]);//後 } void reflect_Cube(Point_3V v,CPoint move,bool draw_Line,bool fill_face,int *c1_fill,int c2,CDC *p){ f[0].reflect_Face(v,move,draw_Line,fill_face,c1_fill[0],c2,p); f[1].reflect_Face(v,move,draw_Line,fill_face,c1_fill[1],c2,p); f[2].reflect_Face(v,move,draw_Line,fill_face,c1_fill[2],c2,p); f[3].reflect_Face(v,move,draw_Line,fill_face,c1_fill[3],c2,p); f[4].reflect_Face(v,move,draw_Line,fill_face,c1_fill[4],c2,p); f[5].reflect_Face(v,move,draw_Line,fill_face,c1_fill[5],c2,p); } void fill( int p){ switch(p){ case 0: {point_num=2+line_num-face_num;} break; //已知其它兩個,求點 case 1:{line_num=point_num+face_num-2;}break;//已知其它兩個,求線 case 2:{face_num=2+line_num-point_num;}break;//已知其它兩個,求面 } } };
void CMy1View::OnDraw(CDC* pDC) { CMy1Doc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: 在此處爲本機數據添加繪製代碼 Point_3V p[8]={ Point_3V(0,0,100),//0 Point_3V(100,0,100),//1 Point_3V(100,100,100),//2 Point_3V(0,100,100),//3 Point_3V(0,0,0),//4 Point_3V(100,0,0),//5 Point_3V(100,100,0),//6 Point_3V(0,100,0)//7 }; //偏移量 CPoint move; move.SetPoint(200,200); //視點 Point_3V v(1,1.2,1); //顏色 int color[6]={RGB(255,0,0),RGB(0,255,0),RGB(0,0,255),RGB(255,255,0),RGB(255,0,255),RGB(0,255,255)}; //cube int point_num=8,face_num=12,line_num=6; cube cb(point_num,face_num,line_num); cb.set_cube(p); cb.reflect_Cube(v,move,true,false,color,0,pDC);//線框模式 //cb.reflect_Cube(v,move,true,true,color,0,pDC);//填充模式 }
實驗結果:this
線框 填充spa
實驗總結:3d
l 建立類Point_3V來存放三維點,Line_2V來存放二維點,Line來存放三維線,Line_2V來存放二維線,face_2V來存放二維面,face來存放三維面,cube來定義一個矩形。以上的二維都是用來平行投影時使用的,三維則是存放在三維空間中真實存在的矩形的信息,且這幾個三維類之間使用了類的繼承來實現。指針
l 矩形點的輸入順序應該按照逆時針來輸入,這是由於每一個面的法向量有兩種結果,可是使用背面檢測法,只有朝向立方體的外部才爲正方向,因此按照逆時針得出這個面上兩個不共線的向量,而後求出二者的叉積就能夠正確得出面的法向量。code
l 立方體投影的實現思路是(假設數據已經正確輸入):
cube(8點,12棱,6面)-> reflect_Cube(進行立方體的投影)-> f[i].reflect_Face(進行 每一個面的投影) -> view_face(判斷這個面是否可見,若是可見則將這個面上的4點設置爲可 見;若是不可見則判斷下個面是否可見) ->l2.reflect_Line(進行一個面4條線條線的投 影)-> end.reflect_Point_3V(進行這條線的兩個點投影)->判斷是否畫線(若是爲真,畫 線;不然不畫)
l 在建立每個三維真實的點,線,面時默認不可見,因爲使用了,類的繼承,因此好比要建立線時,要保證這條線的起點是從基類三維點繼承過來的信息,因此應該使用指針,那麼在線的構造函數中就應該爲這個指針動態的建立一個空間,不然程序執行時,會沒法訪問這個指針的空間。
l 在進行編程時,數學功底要紮實,我在進行兩個向量的叉積運算時,未考慮到矩陣奇、偶列的符號不一樣,因此致使結果爲下圖所示,最後經過斷點測試才知道這個緣由。
面填充時的思路:
將中OnDraw()函數裏的cb.reflect_Cube(v,move,true,false,color,0,pDC);
換位cb.reflect_Cube(v,move,true,true,color,0,pDC);就好。
實驗總結:
不一樣顏色的面使用了數組中存放不一樣的顏色信息來實現,填充方式使用了種子填充,種子的位置使用了四邊形的中心座標。
實驗參考文獻:
http://www.docin.com/touch_new/preview_new.do?id=489294049&html=1
補充:
建議你們在使用種子填充時,使用VC6.0,不要用VS,VS會報錯:(你們若是有好的方法來解決這個問題,請聯繫我:2283320260@qq.com)
歡迎你們一塊兒討論