C++计算几何算法大全



/*
计算几何



目录 


㈠ 点的基本运算 
1. 平面上两点之间距离 1 
2. 判断两点是否重合 1 
3. 矢量叉乘 1 
4. 矢量点乘 2 
5. 判断点是否在线段上 2 
6. 求一点饶某点旋转后的坐标 2 
7. 求矢量夹角 2 

㈡ 线段及直线的基本运算 
1. 点与线段的关系 3 
2. 求点到线段所在直线垂线的垂足 4 
3. 点到线段的最近点 4 
4. 点到线段所在直线的距离 4 
5. 点到折线集的最近距离 4 
6. 判断圆是否在多边形内 5 
7. 求矢量夹角余弦 5 
8. 求线段之间的夹角 5 
9. 判断线段是否相交 6 
10.判断线段是否相交但不交在端点处 6 
11.求线段所在直线的方程 6 
12.求直线的斜率 7 
13.求直线的倾斜角 7 
14.求点关于某直线的对称点 7 
15.判断两条直线是否相交及求直线交点 7 
16.判断线段是否相交,如果相交返回交点 7 

㈢ 多边形常用算法模块 
1. 判断多边形是否简单多边形 8 
2. 检查多边形顶点的凸凹性 9 
3. 判断多边形是否凸多边形 9 
4. 求多边形面积 9 
5. 判断多边形顶点的排列方向,方法一 10 
6. 判断多边形顶点的排列方向,方法二 10 
7. 射线法判断点是否在多边形内 10 
8. 判断点是否在凸多边形内 11 
9. 寻找点集的graham算法 12 
10.寻找点集凸包的卷包裹法 13 
11.判断线段是否在多边形内 14 
12.求简单多边形的重心 15 
13.求凸多边形的重心 17 
14.求肯定在给定多边形内的一个点 17 
15.求从多边形外一点出发到该多边形的切线 18 
16.判断多边形的核是否存在 19 

㈣ 圆的基本运算 
1 .点是否在圆内 20 
2 .求不共线的三点所确定的圆 21 

㈤ 矩形的基本运算 
1.已知矩形三点坐标,求第4点坐标 22 

㈥ 常用算法的描述 22 

㈦ 补充 
1.两圆关系: 24 
2.判断圆是否在矩形内: 24 
3.点到平面的距离: 25 
4.点是否在直线同侧: 25 
5.镜面反射线: 25 
6.矩形包含: 26 
7.两圆交点: 27 
8.两圆公共面积: 28 
9. 圆和直线关系: 29 
10. 内切圆: 30 
11. 求切点: 31 
12. 线段的左右旋: 31 
13.公式: 32 
*/
/* 需要包含的头文件 */ 
#include  

/* 常用的常量定义 */ 
const double	INF		= 1E200    
const double	EP		= 1E-10 
const int		MAXV	= 300 
const double	PI		= 3.14159265 

/* 基本几何结构 */ 
struct POINT 
{ 
	double x; 
	double y; 
	POINT(double a=0, double b=0) { x=a; y=b;} //constructor 
}; 
struct LINESEG 
{ 
	POINT s; 
	POINT e; 
	LINESEG(POINT a, POINT b) { s=a; e=b;} 
	LINESEG() { } 
}; 
struct LINE           // 直线的解析方程 a*x+b*y+c=0  为统一表示,约定 a >= 0 
{ 
   double a; 
   double b; 
   double c; 
   LINE(double d1=1, double d2=-1, double d3=0) {a=d1; b=d2; c=d3;} 
}; 

/**********************
 *                    * 
 *   点的基本运算     * 
 *                    * 
 **********************/ 

double dist(POINT p1,POINT p2)                // 返回两点之间欧氏距离 
{ 
	return( sqrt( (p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y) ) ); 
} 
bool equal_point(POINT p1,POINT p2)           // 判断两个点是否重合  
{ 
	return ( (abs(p1.x-p2.x)0:ep在矢量opsp的逆时针方向; 
r=0:opspep三点共线; 
r<0:ep在矢量opsp的顺时针方向 
*******************************************************************************/ 
double multiply(POINT sp,POINT ep,POINT op) 
{ 
	return((sp.x-op.x)*(ep.y-op.y)-(ep.x-op.x)*(sp.y-op.y)); 
} 
/* 
r=dotmultiply(p1,p2,op),得到矢量(p1-op)和(p2-op)的点积,如果两个矢量都非零矢量 
r<0:两矢量夹角为锐角;
r=0:两矢量夹角为直角;
r>0:两矢量夹角为钝角 
*******************************************************************************/ 
double dotmultiply(POINT p1,POINT p2,POINT p0) 
{ 
	return ((p1.x-p0.x)*(p2.x-p0.x)+(p1.y-p0.y)*(p2.y-p0.y)); 
} 
/****************************************************************************** 
判断点p是否在线段l上
条件:(p在线段l所在的直线上) && (点p在以线段l为对角线的矩形内)
*******************************************************************************/ 
bool online(LINESEG l,POINT p) 
{ 
	return( (multiply(l.e,p,l.s)==0) &&( ( (p.x-l.s.x)*(p.x-l.e.x)<=0 )&&( (p.y-l.s.y)*(p.y-l.e.y)<=0 ) ) ); 
} 
// 返回点p以点o为圆心逆时针旋转alpha(单位:弧度)后所在的位置 
POINT rotate(POINT o,double alpha,POINT p) 
{ 
	POINT tp; 
	p.x-=o.x; 
	p.y-=o.y; 
	tp.x=p.x*cos(alpha)-p.y*sin(alpha)+o.x; 
	tp.y=p.y*cos(alpha)+p.x*sin(alpha)+o.y; 
	return tp; 
} 
/* 返回顶角在o点,起始边为os,终止边为oe的夹角(单位:弧度) 
	角度小于pi,返回正值 
	角度大于pi,返回负值 
	可以用于求线段之间的夹角 
原理:
	r = dotmultiply(s,e,o) / (dist(o,s)*dist(o,e))
	r'= multiply(s,e,o)

	r >= 1	angle = 0;
	r <= -1	angle = -PI
	-10	angle = arccos(r)
	-1=  1.0 ) return 0; 
	if (cosfi <= -1.0 ) return -3.1415926; 

	fi=acos(cosfi); 
	if (dsx*dey-dsy*dex>0) return fi;      // 说明矢量os 在矢量 oe的顺时针方向 
	return -fi; 
} 
  /*****************************\ 
  *                             * 
  *      线段及直线的基本运算   * 
  *                             * 
  \*****************************/ 

/* 判断点与线段的关系,用途很广泛 
本函数是根据下面的公式写的,P是点C到线段AB所在直线的垂足 

                AC dot AB 
        r =     --------- 
                 ||AB||^2 
             (Cx-Ax)(Bx-Ax) + (Cy-Ay)(By-Ay) 
          = ------------------------------- 
                          L^2 

    r has the following meaning: 

        r=0      P = A 
        r=1      P = B 
        r<0		 P is on the backward extension of AB 
		r>1      P is on the forward extension of AB 
        01) 
	{ 
		np=l.e; 
		return dist(p,l.e); 
	} 
	np=perpendicular(p,l); 
	return dist(p,np); 
} 
// 求点p到线段l所在直线的距离,请注意本函数与上个函数的区别  
double ptoldist(POINT p,LINESEG l) 
{ 
	return abs(multiply(p,l.e,l.s))/dist(l.s,l.e); 
} 
/* 计算点到折线集的最近距离,并返回最近点. 
注意:调用的是ptolineseg()函数 */ 
double ptopointset(int vcount,POINT pointset[],POINT p,POINT &q) 
{ 
	int i; 
	double cd=double(INF),td; 
	LINESEG l; 
	POINT tq,cq; 

	for(i=0;i= 0。
//判断Q1Q2跨立P1P2的依据是:( Q1 - P1 ) × ( P2 - P1 ) * ( P2 - P1 ) × ( Q2 - P1 ) >= 0。
bool intersect(LINESEG u,LINESEG v) 
{ 
	return( (max(u.s.x,u.e.x)>=min(v.s.x,v.e.x))&&                     //排斥实验 
			(max(v.s.x,v.e.x)>=min(u.s.x,u.e.x))&& 
			(max(u.s.y,u.e.y)>=min(v.s.y,v.e.y))&& 
			(max(v.s.y,v.e.y)>=min(u.s.y,u.e.y))&& 
			(multiply(v.s,u.e,u.s)*multiply(u.e,v.e,u.s)>=0)&&         //跨立实验 
			(multiply(u.s,v.e,v.s)*multiply(v.e,u.e,v.s)>=0)); 
} 
//  (线段u和v相交)&&(交点不是双方的端点) 时返回true    
bool intersect_A(LINESEG u,LINESEG v) 
{ 
	return	((intersect(u,v))&& 
			(!online(u,v.s))&& 
			(!online(u,v.e))&& 
			(!online(v,u.e))&& 
			(!online(v,u.s))); 
} 
// 线段v所在直线与线段u相交时返回true;方法:判断线段u是否跨立线段v  
bool intersect_l(LINESEG u,LINESEG v) 
{ 
	return multiply(u.s,v.e,v.s)*multiply(v.e,u.e,v.s)>=0; 
} 
// 根据已知两点坐标,求过这两点的直线解析方程: a*x+b*y+c = 0  (a >= 0)  
LINE makeline(POINT p1,POINT p2) 
{ 
	LINE tl; 
	int sign = 1; 
	tl.a=p2.y-p1.y; 
	if(tl.a<0) 
	{ 
		sign = -1; 
		tl.a=sign*tl.a; 
	} 
	tl.b=sign*(p1.x-p2.x); 
	tl.c=sign*(p1.y*p2.x-p1.x*p2.y); 
	return tl; 
} 
// 根据直线解析方程返回直线的斜率k,水平线返回 0,竖直线返回 1e200 
double slope(LINE l) 
{ 
	if(abs(l.a) < 1e-20)
		return 0; 
	if(abs(l.b) < 1e-20)
		return INF; 
	return -(l.a/l.b); 
} 
// 返回直线的倾斜角alpha ( 0 - pi) 
double alpha(LINE l) 
{ 
	if(abs(l.a)< EP)
		return 0; 
	if(abs(l.b)< EP)
		return PI/2; 
	double k=slope(l); 
	if(k>0) 
		return atan(k); 
	else 
		return PI+atan(k); 
} 
// 求点p关于直线l的对称点  
POINT symmetry(LINE l,POINT p) 
{ 
   POINT tp; 
   tp.x=((l.b*l.b-l.a*l.a)*p.x-2*l.a*l.b*p.y-2*l.a*l.c)/(l.a*l.a+l.b*l.b); 
   tp.y=((l.a*l.a-l.b*l.b)*p.y-2*l.a*l.b*p.x-2*l.b*l.c)/(l.a*l.a+l.b*l.b); 
   return tp; 
} 
// 如果两条直线 l1(a1*x+b1*y+c1 = 0), l2(a2*x+b2*y+c2 = 0)相交,返回true,且返回交点p  
bool lineintersect(LINE l1,LINE l2,POINT &p) // 是 L1,L2 
{ 
	double d=l1.a*l2.b-l2.a*l1.b; 
	if(abs(d)=0 ) 
			bc[(index+1)%vcount]=1; 
		else 
			bc[(index+1)%vcount]=0; 
		index++; 
		count--; 
	} 
}
// 返回值:多边形polygon是凸多边形时,返回true  
bool isconvex(int vcount,POINT polygon[]) 
{ 
	bool bc[MAXV]; 
	checkconvex(vcount,polygon,bc); 
	for(int i=0;i0; 
} 
// 另一种判断多边形顶点排列方向的方法  
bool isccwize(int vcount,POINT polygon[]) 
{ 
	int i,index; 
	POINT a,b,v; 
	v=polygon[0]; 
	index=0; 
	for(i=1;i0; 
} 
/********************************************************************************************   
射线法判断点q与多边形polygon的位置关系,要求polygon为简单多边形,顶点逆时针排列 
   如果点在多边形内:   返回0 
   如果点在多边形边上: 返回1 
   如果点在多边形外:	返回2 
*********************************************************************************************/ 
int insidepolygon(int vcount,POINT Polygon[],POINT q) 
{ 
	int c=0,i,n; 
	LINESEG l1,l2; 
	bool bintersect_a,bonline1,bonline2,bonline3; 
	double r1,r2; 

	l1.s=q; 
	l1.e=q; 
	l1.e.x=double(INF); 
	n=vcount; 
	for (i=0;i0) ||    
		(bonline3=online(l1,Polygon[(i+2)%n]))&&     /* 下一条边是水平线,前一个端点和后一个端点在射线两侧  */ 
			((r2=multiply(Polygon[i],Polygon[(i+2)%n],l1.s)*multiply(Polygon[(i+2)%n], 
		Polygon[(i+3)%n],l1.s))>0) 
				) 
			) 
		) c++; 
	} 
	if(c%2 == 1) 
		return 0; 
	else 
		return 2; 
} 
//点q是凸多边形polygon内时,返回true;注意:多边形polygon一定要是凸多边形  
bool InsideConvexPolygon(int vcount,POINT polygon[],POINT q) // 可用于三角形! 
{ 
	POINT p; 
	LINESEG l; 
	int i; 
	p.x=0;p.y=0; 
	for(i=0;i0 ||  // 极角更小    
				(multiply(PointSet[j],PointSet[k],PointSet[0])==0) && /* 极角相等,距离更短 */        
				dist(PointSet[0],PointSet[j])=0) 
			top--; 
		ch[++top]=PointSet[i]; 
	} 
	len=top+1; 
} 
// 卷包裹法求点集凸壳,参数说明同graham算法    
void ConvexClosure(POINT PointSet[],POINT ch[],int n,int &len) 
{ 
	int top=0,i,index,first; 
	double curmax,curcos,curdis; 
	POINT tmp; 
	LINESEG l1,l2; 
	bool use[MAXV]; 
	tmp=PointSet[0]; 
	index=0; 
	// 选取y最小点,如果多于一个,则选取最左点 
	for(i=1;icurmax || fabs(curcos-curmax)<1e-6 && dist(l2.s,l2.e)>curdis) 
			{ 
				curmax=curcos; 
				index=i; 
				curdis=dist(l2.s,l2.e); 
			} 
		} 
		use[first]=false;            //清空第first个顶点标志,使最后能形成封闭的hull 
		use[index]=true; 
		ch[top++]=PointSet[index]; 
		l1.s=ch[top-2]; 
		l1.e=ch[top-1]; 
		l2.s=ch[top-1]; 
	} 
	len=top-1; 
} 
/*********************************************************************************************  
	判断线段是否在简单多边形内(注意:如果多边形是凸多边形,下面的算法可以化简) 
   	必要条件一:线段的两个端点都在多边形内; 
	必要条件二:线段和多边形的所有边都不内交; 
	用途:	1. 判断折线是否在简单多边形内 
			2. 判断简单多边形是否在另一个简单多边形内 
**********************************************************************************************/ 
bool LinesegInsidePolygon(int vcount,POINT polygon[],LINESEG l) 
{ 
	// 判断线端l的端点是否不都在多边形内 
	if(!insidepolygon(vcount,polygon,l.s)||!insidepolygon(vcount,polygon,l.e)) 
		return false; 
	int top=0,i,j; 
	POINT PointSet[MAXV],tmp; 
	LINESEG s; 

	for(i=0;iPointSet[j].x || fabs(PointSet[i].x-PointSet[j].x)PointSet[j].y ) 
			{ 
				tmp=PointSet[i]; 
				PointSet[i]=PointSet[j]; 
				PointSet[j]=tmp; 
			} 
		} 
	} 

	for(i=0;i x1 ) 
	{ 
		AddPosPart ((x2+x1)/2, y1/2, (x2-x1) * y1,xtr,ytr,wtr); /* rectangle 全局变量变化处 */ 
		AddPosPart ((x1+x2+x2)/3, (y1+y1+y2)/3, (x2-x1)*(y2-y1)/2,xtr,ytr,wtr);    
		// triangle 全局变量变化处 
	} 
	else 
	{ 
		AddNegPart ((x2+x1)/2, y1/2, (x2-x1) * y1,xtl,ytl,wtl);  
		// rectangle 全局变量变化处 
		AddNegPart ((x1+x2+x2)/3, (y1+y1+y2)/3, (x2-x1)*(y2-y1)/2,xtl,ytl,wtl);  
		// triangle  全局变量变化处 
	} 
} 
POINT cg_simple(int vcount,POINT polygon[]) 
{ 
	double xtr,ytr,wtr,xtl,ytl,wtl;        
	//注意: 如果把xtr,ytr,wtr,xtl,ytl,wtl改成全局变量后这里要删去 
	POINT p1,p2,tp; 
	xtr = ytr = wtr = 0.0; 
	xtl = ytl = wtl = 0.0; 
	for(int i=0;i=4的简单多边形至少有一条对角线 
结论	: x坐标最大,最小的点肯定是凸顶点 
	y坐标最大,最小的点肯定是凸顶点            
************************************************/ 
POINT a_point_insidepoly(int vcount,POINT polygon[]) 
{ 
	POINT v,a,b,r; 
	int i,index; 
	v=polygon[0]; 
	index=0; 
	for(i=1;i=0;                // p is to the left of pre edge 
		bln=multiply(en.e,p,en.s)>=0;                // p is to the left of next edge 
		if(!blp&&bln) 
		{ 
			if(multiply(polygon[i],rp,p)>0)           // polygon[i] is above rp 
			rp=polygon[i]; 
		} 
		if(blp&&!bln) 
		{ 
			if(multiply(lp,polygon[i],p)>0)           // polygon[i] is below lp 
			lp=polygon[i]; 
		} 
	} 
	return ; 
} 
// 如果多边形polygon的核存在,返回true,返回核上的一点p.顶点按逆时针方向输入  
bool core_exist(int vcount,POINT polygon[],POINT &p) 
{ 
	int i,j,k; 
	LINESEG l; 
	LINE lineset[MAXV]; 
	for(i=0;i0)      
					//多边形顶点按逆时针方向排列,核肯定在每条边的左侧或边上 
					break; 
				} 
				if(k == vcount)             //找到了一个核上的点 
				break; 
			} 
		} 
		if(j r1+r2 ) 
		return 1; 
	if( d < fabs(r1-r2) ) 
		return 5; 
	if( fabs(r1-r2) < d && d < r1+r2 ) 
		return 3; 
	return 0; // indicate an error! 
} 
//判断圆是否在矩形内:
// 判定圆是否在矩形内,是就返回true(设矩形水平,且其四个顶点由左上开始按顺时针排列) 
// 调用ptoldist函数,在第4页 
bool CircleRecRelation(POINT pc, double r, POINT pr1, POINT pr2, POINT pr3, POINT pr4) 
{ 
	if( pr1.x < pc.x && pc.x < pr2.x && pr3.y < pc.y && pc.y < pr2.y ) 
	{ 
		LINESEG line1(pr1, pr2); 
		LINESEG line2(pr2, pr3); 
		LINESEG line3(pr3, pr4); 
		LINESEG line4(pr4, pr1); 
		if( r 0; 
}
//镜面反射线:
// 已知入射线、镜面,求反射线。 
// a1,b1,c1为镜面直线方程(a1 x + b1 y + c1 = 0 ,下同)系数;  
//a2,b2,c2为入射光直线方程系数;  
//a,b,c为反射光直线方程系数. 
// 光是有方向的,使用时注意:入射光向量:<-b2,a2>;反射光向量:. 
// 不要忘记结果中可能会有"negative zeros" 
void reflect(double a1,double b1,double c1,double a2,double b2,double c2,double &a,double &b,double &c) 
{ 
	double n,m; 
	double tpb,tpa; 
	tpb=b1*b2+a1*a2; 
	tpa=a2*b1-a1*b2; 
	m=(tpb*b1+tpa*a1)/(b1*b1+a1*a1); 
	n=(tpa*b1-tpb*a1)/(b1*b1+a1*a1); 
	if(fabs(a1*b2-a2*b1)<1e-20) 
	{ 
		a=a2;b=b2;c=c2; 
		return; 
	} 
	double xx,yy; //(xx,yy)是入射线与镜面的交点。 
	xx=(b1*c2-b2*c1)/(a1*b2-a2*b1); 
	yy=(a2*c1-a1*c2)/(a1*b2-a2*b1); 
	a=n; 
	b=-m; 
	c=m*yy-xx*n; 
} 
//矩形包含: 
// 矩形2(C,D)是否在1(A,B)内
bool r2inr1(double A,double B,double C,double D)  
{ 
	double X,Y,L,K,DMax; 
	if (A < B) 
	{ 
		double tmp = A; 
		A = B; 
		B = tmp; 
	} 
	if (C < D) 
	{ 
		double tmp = C; 
		C = D; 
		D = tmp; 
	} 
	if (A > C && B > D)                 // trivial case  
		return true; 
	else 
		if (D >= B) 
			return false; 
		else 
		{ 
			X = sqrt(A * A + B * B);         // outer rectangle's diagonal  
			Y = sqrt(C * C + D * D);         // inner rectangle's diagonal  
			if (Y < B) // check for marginal conditions 
				return true; // the inner rectangle can freely rotate inside 
			else 
				if (Y > X) 
					return false; 
				else 
				{ 
					L = (B - sqrt(Y * Y - A * A)) / 2; 
					K = (A - sqrt(Y * Y - B * B)) / 2; 
					DMax = sqrt(L * L + K * K); 
					if (D >= DMax) 
					return false; 
					else 
					return true; 
				} 
		} 
} 
//两圆交点: 
// 两圆已经相交(相切) 
void  c2point(POINT p1,double r1,POINT p2,double r2,POINT &rp1,POINT &rp2) 
{ 
	double a,b,r; 
	a=p2.x-p1.x; 
	b=p2.y-p1.y; 
	r=(a*a+b*b+r1*r1-r2*r2)/2; 
	if(a==0&&b!=0) 
	{ 
		rp1.y=rp2.y=r/b; 
		rp1.x=sqrt(r1*r1-rp1.y*rp1.y); 
		rp2.x=-rp1.x; 
	} 
	else if(a!=0&&b==0) 
	{ 
		rp1.x=rp2.x=r/a; 
		rp1.y=sqrt(r1*r1-rp1.x*rp2.x); 
		rp2.y=-rp1.y; 
	} 
	else if(a!=0&&b!=0) 
	{ 
		double delta; 
		delta=b*b*r*r-(a*a+b*b)*(r*r-r1*r1*a*a); 
		rp1.y=(b*r+sqrt(delta))/(a*a+b*b); 
		rp2.y=(b*r-sqrt(delta))/(a*a+b*b); 
		rp1.x=(r-b*rp1.y)/a; 
		rp2.x=(r-b*rp2.y)/a; 
	} 

	rp1.x+=p1.x; 
	rp1.y+=p1.y; 
	rp2.x+=p1.x; 
	rp2.y+=p1.y; 
} 
//两圆公共面积:
// 必须保证相交 
double c2area(POINT p1,double r1,POINT p2,double r2) 
{ 
	POINT rp1,rp2; 
	c2point(p1,r1,p2,r2,rp1,rp2); 

	if(r1>r2) //保证r2>r1 
	{ 
		swap(p1,p2); 
		swap(r1,r2); 
	} 
	double a,b,rr; 
	a=p1.x-p2.x; 
	b=p1.y-p2.y; 
	rr=sqrt(a*a+b*b); 

	double dx1,dy1,dx2,dy2; 
	double sita1,sita2; 
	dx1=rp1.x-p1.x; 
	dy1=rp1.y-p1.y; 
	dx2=rp2.x-p1.x; 
	dy2=rp2.y-p1.y; 
	sita1=acos((dx1*dx2+dy1*dy2)/r1/r1); 

	dx1=rp1.x-p2.x; 
	dy1=rp1.y-p2.y; 
	dx2=rp2.x-p2.x; 
	dy2=rp2.y-p2.y; 
	sita2=acos((dx1*dx2+dy1*dy2)/r2/r2); 
	double s=0; 
	if(rr0) 
		return -1; 
	else 
		return 1; 
} 
/*
公式: 

球坐标公式: 
直角坐标为 P(x, y, z) 时,对应的球坐标是(rsinφcosθ, rsinφsinθ, rcosφ),其中φ是向量OP与Z轴的夹角,范围[0,π];是OP在XOY面上的投影到X轴的旋角,范围[0,2π]  

直线的一般方程转化成向量方程: 
ax+by+c=0 
x-x0     y-y0 
   ------ = ------- // (x0,y0)为直线上一点,m,n为向量 
m        n 
转换关系: 
a=n;b=-m;c=m·y0-n·x0; 
m=-b; n=a; 

三点平面方程: 
三点为P1,P2,P3 
设向量  M1=P2-P1; M2=P3-P1; 
平面法向量:  M=M1 x M2 () 
平面方程:    M.i(x-P1.x)+M.j(y-P1.y)+M.k(z-P1.z)=0 
*/


你可能感兴趣的:(C++,计算机图形图像)