作者:feiquan 出处:http://www.cnblogs.com/feiquan/ 版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 大家写文都不容易,请尊重劳动成果~ 这里谢谢大家啦(*/ω\*)
大家可以先看下我的上一篇文章:对于单个模型(长方体为例)进行面投影时的消隐 http://www.cnblogs.com/feiquan/p/8213875.html
这两篇有相同的地方。
要求:
使用鼠标左键,按下,再抬起,连续3次绘制出三棱锥的地面,再按下左键抬起绘制出三棱锥的顶点,并绘制出三棱锥。(利用背面检测法)
思路:
和上一篇中立方体的投影一样,我们要先获取并存储三棱锥的相关信息,只不过这里是使用了鼠标来获得。剩下的就和之前的差不多了,我在之前的程序上修改后得到的。
相关类定义:
//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.x=(int)a; p2.y=(int)b; //p2.SetPoint((int)a,(int)b); return p2; } }; //二维线 class Line_2V{ public : CPoint start,end; Line_2V(){ start.x=0;start.y=0; end.x=0,end.y=0; //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); } void fill_CClientDC(CClientDC *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(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; } //三维线投影 Line_2V reflect_Line_CClientDC(Point_3V v,CPoint move,bool draw,CClientDC *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_CClientDC(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; face_2V(){ } face_2V(Line_2V i,Line_2V j,Line_2V k ){ a=i;b=j;c=k; } void B(int x,int y,int c1_fill,int c2,CDC *p){ //种子填充 long 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 B_CClientDC(int x,int y,int c1_fill,int c2,CClientDC *p){ //种子填充 long 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); CPoint a_cent((a.start.x+a.end.x)/2,(a.start.y+a.end.y)/2),c_cent((c.start.x+c.end.x)/2,(c.start.y+c.end.y)/2); CPoint cent((a_cent.x+c_cent.x)/2,(a_cent.y+c_cent.y)/2); B(cent.x,cent.y, c1_fill, c2,p); } void fill_CClientDC(int c1_fill,int c2,CClientDC *p){ a.fill(p);b.fill(p);c.fill(p); CPoint a_cent((a.start.x+a.end.x)/2,(a.start.y+a.end.y)/2),c_cent((c.start.x+c.end.x)/2,(c.start.y+c.end.y)/2); CPoint cent((a_cent.x+c_cent.x)/2,(a_cent.y+c_cent.y)/2); B_CClientDC(cent.x,cent.y, c1_fill, c2,p); } }; //基于点填充面(不会开辟新空间) class face :public Line{ public : Point_3V *p;//逆时针 Line *l1,l2,l3;//l1只能是指针,为了是其与点公用一个存储空间 bool v; face(){ p=new Point_3V[3]; l1=new Line[1]; v=false; } face(Point_3V *q[3]){ this->start=q[0]; this->end=*q[1]; p=new Point_3V[3]; 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[3]); } face(Point_3V a,Point_3V b,Point_3V c,Point_3V d){ p=new Point_3V[3]; 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[3]); } face( face *p1){ p=new Point_3V[3]; 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; } void set_Point(Point_3V q[3]){ for(int i=0;i<3;i++){ p[i]=q[i]; } } void set_Face(Point_3V q[3]){ for(int i=0;i<3;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[3]); } void set_Face(Point_3V q1,Point_3V q2,Point_3V q3){ p[0]=q1; p[1]=q2; p[2]=q3; l1->set_Line(p[0],p[1]); l2.set_Line(p[1],p[2]); l3.set_Line(p[2],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); if(fill_face){ face_2V f2(l2_1,l2_2,l2_3); f2.fill(c1_fill,c2,p); } } } //3V面投影 void reflect_Face_CClientDC(Point_3V v,CPoint move,bool draw_Line,bool fill_face,int c1_fill,int c2,CClientDC *p){ if(view_face(this,v)){ Line_2V l2_1=l1->reflect_Line_CClientDC(v,move,draw_Line,p); Line_2V l2_2=l2.reflect_Line_CClientDC(v,move,draw_Line,p); Line_2V l2_3=l3.reflect_Line_CClientDC(v,move,draw_Line,p); if(fill_face){ face_2V f2(l2_1,l2_2,l2_3); f2.fill_CClientDC(c1_fill,c2,p); } } } }; //多边形 p+f-l=2 class cube{ private: bool isCube; public : int point_num,face_num,line_num; Point_3V p[4]; Line l[6]; face f[4]; 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]);//底 f[1].set_Face( p[0],p[3],p[1]);//四周 f[2].set_Face(p[0],p[2],p[3]);//四周 f[3].set_Face(p[3],p[2],p[1]);//四周 } 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); } void reflect_Cube_CClientDC(Point_3V v,CPoint move,bool draw_Line,bool fill_face,int *c1_fill,int c2,CClientDC *p){ f[0].reflect_Face_CClientDC(v,move,draw_Line,fill_face,c1_fill[0],c2,p); f[1].reflect_Face_CClientDC(v,move,draw_Line,fill_face,c1_fill[1],c2,p); f[2].reflect_Face_CClientDC(v,move,draw_Line,fill_face,c1_fill[2],c2,p); f[3].reflect_Face_CClientDC(v,move,draw_Line,fill_face,c1_fill[3],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;//已知其它两个,求面 } } };
定义全局变量:
//判断鼠标左键次数 int count=0; //鼠标左键的点 CPoint start,end; //临时面 face di; //点 Point_3V p[4]; //偏移量 CPoint move; //视点 Point_3V v(1,1.2,1); //颜色 int color[4]={RGB(255,0,0),RGB(0,255,0),RGB(0,0,255),RGB(255,255,0)}; //cube int point_num=4,face_num=6,line_num=4; cube cb(point_num,face_num,line_num);
鼠标事件:
void CMy3View::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: ?ú′?ìí?ó???¢′|àí3ìDò′ú??oí/?òμ÷ó???è??μ CView::OnLButtonDown(nFlags, point); start=point; if(count==4){ count=0; } } void CMy3View::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: ?ú′?ìí?ó???¢′|àí3ìDò′ú??oí/?òμ÷ó???è??μ double z=0; //偏移量 move.x=0; move.y=0; //move.SetPoint(200,200); CClientDC dc(this); CView::OnLButtonUp(nFlags, point); end=point; switch(count){ case 0:{ p[0].set_Point(start.x,start.y,z); }break; case 1:{ p[1].set_Point(start.x,start.y,z); }break; case 2:{ p[2].set_Point(start.x,start.y,z); di.set_Face(p[0],p[1],p[2]); di.reflect_Face_CClientDC(v,move,true,false,color[0],0,&dc); }break; case 3: { p[3].set_Point(start.x,start.y,z); //擦除 CPen mypen,*oldpen; mypen.CreatePen(1,2,RGB(255,255,255)); oldpen=dc.SelectObject(&mypen); di.reflect_Face_CClientDC(v,move,true,false,color[0],0,&dc); dc.SelectObject(oldpen); //cube cb.set_cube(p); //cb.reflect_Cube_CClientDC(v,move,true,false,color,0,&dc); cb.reflect_Cube_CClientDC(v,move,true,true,color,0,&dc); }break; } count++; }
实验结果:
(1)
实验总结:
l 这个程序的实现主要使用同第一题一样的思路,只是将立方体换成了三棱锥。
l 对这个三棱锥的数据的填充主要是靠鼠标的点击来获取,由于三棱锥只有4个点所以使用了count来计数,当其达到4时归零,重新计数。使用switch语句实现,当count的数不同时执行不同的语句。Count=1、2计数,count=3,判断底面是否可见并画线,count=4,进行整个三棱锥的投影。
程序的不足:
在三棱锥的数据填充时,没有对填充的点进行排序,来保证前三个点构成的面一定是底面,这个程序只是构建了三棱锥并投影(大家如果有好的方法来解决这个问题,请联系我:2283320260@qq.com)。
补充:
建议大家在使用种子填充时,使用VC6.0,不要用VS,VS会报错:(大家如果有好的方法来解决这个问题,请联系我:2283320260@qq.com)