OpenGL nrmath download here: http://rorger.download.csdn.net/ 将常用的一些算法集合下,文笔功底不够深厚,请见谅 //nrmath.h //rorger, 2011 /* Any problem,contact me:rorger@hotmail.com last modified:5/7/2011*/ #ifndef NRMATH_H #define NRMATH_H #include "nrglbase.h" 辅助// //delta 表示精度误差的绝对值 bool IsEqual(float lhs,float rhs,float delta=0.001); /线段、射线、直线/ //用四个点表示两条线段,如果线段没有焦点(直线有相交),返回0 ,如果有,则返回1. int NRSegIntersect(NRPoint2 A,NRPoint2 B,NRPoint2 C,NRPoint2 D,NRPoint2 &InterPtr) ; //取得两条线段AB和CD击中时间 t 和 u int NRSystemOfLinearEquationOfTwo(NRVector2 b,NRVector2 c,NRVector2 d,float& t,float& u); //取得两条线段AB和CD击中时间 t 和 u int NRSystemOfLinearEquationOfTwo(float bx,float by, float cx,float cy,float dx,float dy,float& t,float& u); //取得射线AC和直线B&n的交点 int NRGetLinesIntersect(NRPoint2 A, NRVector2 c,NRPoint2 B,NRVector2 n, float& tHit,NRPoint2& PHit); //取得向量a经直线反射后的向量,该直线以n为法向量 int NRGetReflectVector(NRVector2 a,NRVector2 n, NRVector2& r); 截取// //线段截取(二维) int NRChopCI(double&tIn,double&tOut,double number,double denom); //线段和凸多边形的交点 (二维) int NRCyrusBeckClip(NRLine2Segment& seg,NRLine2List& L); //截取射线 int NRChopCIRadial(float& tIn ,float& tOut ,float number,float denom,NRVector2 norm,NRVector2& normInHit,NRVector2& normOutHit); //截取射线,重载,要求初始化:tIn=0.0,tOut=-1.0 int NRChopCIRadial(float&tIn,float&tOut,float number,float denom); //凸多边形截取射线,要求初始化:tIn=0.0,tOut=-1.0 int NRCyrusBeckClipRadial(NRPoint2 S,NRVector2 c,NRLine2List& L,float& tIn ,float& tOut ,NRPoint2& pIn,NRPoint2& pOut,NRVector2& normIn,NRVector2& normOut); //凸多边形截取射线,重载 int NRCyrusBeckClipRadial(NRPoint2 S,NRVector2 c,NRLine2List& L,NRPoint2& pIn,NRPoint2& pOut); //多个凸多边形截取射线(密室的情况,其它尚未完善) int NRCyrusBeckClipRadial(NRPoint2 S,NRVector2 c,NRPolygonList& polygonList,NRPoint2& pOut ,NRVector2& norm); /三角形// //判断是否为三角形 int NRIsTriangle(NRPoint2 A,NRPoint2 B,NRPoint2 C); //取得三角形的内切圆和边的三个交点 int NRGetTangentPoints(NRPoint2 A,NRPoint2 B,NRPoint2 C,NRPoint2& R,NRPoint2& S,NRPoint2& T); //取得三角形的外接圆 int NRGetCircumCenter (NRPoint2 A,NRPoint2 B,NRPoint2 C, NRPoint2& S,float& radius); //分别取得三角形三条边的中点 int NRGetCenterPoint(NRPoint2 A,NRPoint2 B,NRPoint2 C,NRPoint2& abP,NRPoint2& bcP, NRPoint2& caP); //分别取得三角形三条边的垂心 int NRGetPerpendicularPoint(NRPoint2 A, NRPoint2 B,NRPoint2 C,NRPoint2& abP,NRPoint2&bcP,NRPoint2& caP); //计算各个端点和垂足连线中点(P为垂足) void NRGetCenterOfPointIntersectPerPendicularPoint(NRPoint2 A,NRPoint2 B,NRPoint2 C,NRPoint2 P,NRPoint2& apP,NRPoint2& bpP,NRPoint2& cpP); //计算各个端点和垂足连线中点(重载) void NRGetCenterOfPointIntersectPerPendicularPoint(NRPoint2 A,NRPoint2 B,NRPoint2 C,NRPoint2& apP,NRPoint2& bpP,NRPoint2& cpP); #endif //NRMATH_H 实现: //mymath.cpp //rorger, 2011 /* Any problem,contact me:rorger@hotmail.com last modified:5/7/2011*/ #include "nrmath.h" #include "math.h" 辅助// //判断两个浮点数是否约等 /*delta: float - 默认值为0.001 return value: bool - 只要lhs和rhs的差的绝对值不超过delta,返回true,否则返回false */ bool IsEqual(float lhs,float rhs,float delta) { if (fabs(lhs-rhs)<=delta) { return true; } else return false; } /线段、射线、直线/ //用四个点表示两条线段,如果线段没有焦点(直线有相交),返回0 ,如果有,则返回1. /* A: NRPoint2 - 线段AB的一个端点 B: NRPoint2 - 线段AB的一个端点 C: NRPoint2 - 线段AB的一个端点 D: NRPoint2 - 线段AB的一个端点 InterPtr: NRPoint2& - 线段的交点 return value: int - 成功返回1,否则返回0 */ int NRSegIntersect(NRPoint2 A,NRPoint2 B,NRPoint2 C,NRPoint2 D,NRPoint2 &InterPtr) { float t,u ; int result ; NRVector2 b; b.set(B.x-A.x,B.y-A.y) ; NRVector2 c ; c.set(C.x-A.x,C.y-A.y) ; NRVector2 d; d.set(D.x-C.x,D.y-C.y) ; result=NRSystemOfLinearEquationOfTwo(b,c,d,t,u); switch(result) { case 1: //both t or u are ok,here we use t InterPtr.x=A.x+t*b.x; InterPtr.y=A.y+t*b.y; break; case -1: break; case 0: //we also calculate the intersect point InterPtr.x=A.x+t*b.x; InterPtr.y=A.y+t*b.y; break; default: break; } return result; } 取得两条线段AB和CD击中时间 t 和 u //形式如:6t=5/2+7/2u // -7/2t=-3/2+5/2u /* bx: float - 向量b的横坐标 by: float - 向量b的纵坐标 cx: float - 向量c的横坐标 cy: float - 向量c的纵坐标 dx: float - 向量d的横坐标 dy: float - 向量d的纵坐标 */ int NRSystemOfLinearEquationOfTwo(float bx,float by, float cx,float cy,float dx,float dy,float& t,float& u) { NRVector2 b ; b.set(bx,by) ; NRVector2 c ; c.set(cx,cy) ; NRVector2 d; d.set(dx,dy) ; return NRSystemOfLinearEquationOfTwo(b,c,d,t,u); } //取得两条线段AB和CD击中时间 t 和 u /* b: NRVector2 - 向量b=B-A c: NRVector2 - 向量c=C-A d: NRVector2 - 向量d=D-C t: float& - 取得AB上的击中时间 u: float& - 取得CD上的击中时间 */ int NRSystemOfLinearEquationOfTwo(NRVector2 b,NRVector2 c,NRVector2 d,float& t,float& u) { //判断向量a和d是否平行,先归一化向量 NRVector2 unia ; NRVector2 unid; unia.x=b.x/(sqrt(b.x*b.x+b.y*b.y)); //分母比不为0,因为如果为0就是0向量了,而这在实际情况没有意义 unia.y=b.y/(sqrt(b.x*b.x+b.y*b.y)); unid.x=d.x/(sqrt(d.x*d.x+d.y*d.y)); unid.y=d.y/(sqrt(d.x*d.x+d.y*d.y)); if (unia.x == unid.x && unia.y==unid.y) { return -1 ; //没有交点,线段平行,有无数的解 } //计算u //a.x==0和a.y==0不会同时出现 if(b.x==0)// 无需判断if(a.x==0 && d.x!=0),因为如果a.x,d.x都为0,必然是平行的情况 { u=-c.x/d.x ; } else if (b.y==0)// && d.y!=0) { u=-c.y/d.y; } else if (b.x !=0 && b.y != 0) { //方程组1两边同时除以a.x //方程组2两边同时除以a.y //另方程组相等即:c.x/a.x+d.x/a.x *u = c.y/a.y+d.y*u /a.y; u=(c.y/b.y-c.x/b.x)/(d.x/b.x-d.y/b.y) ; } //计算t //d.x==0 && d.y==0是不会同时出现的,因为(0,0)向量在这里没有意义 if (d.x==0)// && a.x!=0) { t=c.x/b.x; } else if (d.y ==0 )//&& a.y!=0) { t=c.y/b.y; } else if(d.x !=0 && d.y != 0) { //计算t //方程组1两边同时除以d.x //方程组两边同时除以d.y // 同上得:a.x/d.x*t-c.x/d.x=a.y/d.y*t-c.y/d.y t=(c.x/d.x-c.y/d.y)/(b.x/d.x-b.y/d.y) ; } //如果线段有交点,那么t和u必然介于0和1之间 if(0<=t&&t<=1 && 0<=u&&u<=1) { return 1;//线段有交点 } else if (t<0 || t>1 || u<0 || u>1) { return 0;//线段没有交点,但是线段所在的直线有交点 } return -9999 ; } //取得射线AC和直线B&n的交点 /* 射线表示为A+c*t; 直线表示为点法向量表示:直线上一点B以及直线法向量n A: NRPoint2 - 射线Ac的端点 c: NRVector2 - 射线的方向向量 B: NRPoint2 - 直线上的一点 n: NRVector2 - 直线的法向量 tHit: float& - 取得射线击中直线时的tHit PHit: NRPoint2& - 取得射线击中直线时的PHit */ int NRGetLinesIntersect(NRPoint2 A, NRVector2 c,NRPoint2 B,NRVector2 n, float& tHit,NRPoint2& PHit) { if (n*c==0) { //射线与直线平行 return 0 ; } else { tHit= (n*(B-A))/(n*c); PHit=A+c*tHit; if(n*c >0) { return 1; //射线沿着法向量方向 } else { return -1 ; //射线与法向量相反 } } } //取得向量a经直线反射后的向量,该直线以n为法向量 /* a: NRVector2 -射入向量 n: NRVector2 -直线法向量 r: NRVector2 -a经过直线反射后的向量 */ int NRGetReflectVector(NRVector2 a,NRVector2 n, NRVector2& r) { if(n.GetLength() != 0) { n.Normalized(); //对n进行归一化,以便于利用公式 r=a-2*(a*n)*n; //公式请参考计算机图像学,这里a不要求归一化 return 1; } else return 0 ; } 截取// //线段截取 /* //这个函数使用了公式tHit=n*(B-A)/(n*c)其中c是线段方向,B为截取直线上一点 tIn: double& - 取得射入时击中时间 tOut: double& - 取得射出时击中时间,要求tOut刚刚开始赋值为1.0 number: double - 分子 denom: double - 分母 return value: int - 成功返回1,否则返回0 */ int NRChopCI(double& tIn,double& tOut,double number,double denom) { double tHit ; if(denom < 0) //射线是射入 { tHit = number / denom ; if (tHit > tOut) { return 0 ; } else if(tHit>tIn) { tIn = tHit; } } else if(denom > 0) { tHit = number / denom; if (tHit<tIn) { return 0 ; } if (tHit<tOut) { tOut = tHit; } } else if(number<=0) //denom为0,射线与直线平行 { return 0 ; //在外半空间 } return 1; } //线段和凸多边形的交点 (二维) /* seg: NRLine2Segment& - 线段类 L: NRLine2List& - 点法向量表示的凸多边形 return value: int - 成功返回1,否则0 */ int NRCyrusBeckClip(NRLine2Segment& seg,NRLine2List& L) { double number,denom; double tIn = 0.0,tOut = 1.0 ; //候选区间为[0,1] NRVector2 c, tmp ; c = seg.second-seg.first; //直线的参数表示 R(t)=A+c*tHit for (int i=0;i<L.num();i++) { //利用公式 tHit=n*(B-A)/(n*c) 其中A和c是目的直线参数表示,B和n是另一条直线点法向量 tmp = L.line[i].pt - seg.first; number = L.line[i].norm * tmp ; denom = L.line[i].norm * c ; if (!NRChopCI(tIn,tOut,number,denom)) { return 0 ; //提前结束 } } //注意tIn和tOut都是基于A+c*tHit ;所以在计算seg截点时,先计算seg.second; //否则,A的值,即seg.first就变化了 if(tOut <1.0) { seg.second = seg.first + c * (float)tOut; } if (tIn>0.0) { seg.first = seg.first + c * (float)tIn; } return 1; } //截取射线 /* //这个函数使用了公式tHit=n*(B-A)/(n*c)其中c是线段方向,B为截取直线上一点 tIn: float& - 取得射入点击中时间 tOut: float& - 取得射出点击中时间 number: float - 分子 denom: float - 分母 norm: Vector - 直线的法向量 normInHit: NRVector2& - 如果射入点在边上,取得射入时击中边的法向量 normOutHit: NRVector2& - 如果射出点在边上,取得射出时击中边的法向量 return value: int - 成功返回1,否则0 */ int NRChopCIRadial(float&tIn,float&tOut,float number,float denom,NRVector2 norm,NRVector2& normInHit,NRVector2& normOutHit) { float tHit ; if(denom < 0) //射线是射入 { tHit = number / denom ; if (tOut != -1.0) { if (tHit >tOut) { return 0 ; } } if(tHit>=tIn) { tIn = tHit; normInHit = norm; } } else if(denom > 0) //射线射出 { tHit = number / denom; if (tHit<tIn) { return 0 ; } if (tOut==-1.0) //这是第一次初始化使用的 { tOut = tHit; normOutHit = norm; } else if (tHit<tOut) //最早出去的 { tOut = tHit; normOutHit = norm; } } else if(number<=0) //denom为0,射线与直线平行 { return 0 ; //在外半空间 } return 1; } //截取射线,重载 /* //这个函数使用了公式tHit=n*(B-A)/(n*c)其中c是线段方向,B为截取直线上一点 tIn: float& - 取得射入点时的时间 tOut: float& - 取得射出点时的时间 number: float - 分母 denom: float -分子 return value: int - 成功返回1,否则0 */ int NRChopCIRadial(float&tIn,float&tOut,float number,float denom) { float tHit ; if(denom < 0) //射线是射入 { tHit = number / denom ; if (tOut != -1.0) { if (tHit > tOut) { return 0 ; } } if(tHit>tIn) { tIn = tHit; } } else if(denom > 0) //射线射出 { tHit = number / denom; if (tHit<tIn) { return 0 ; } if (tOut==-1.0) //这是第一次初始化使用的 { tOut = tHit; } else if (tHit<tOut) { tOut = tHit; } } else if(number<=0) //denom为0,射线与直线平行 { return 0 ; //在外半空间 } return 1; } //凸多边形截取射线 /* S: NRPoint2 - 射线端点 c: Vector - 射线方向 L: NRLine2List&- 多边形截取区域 tIn: float& - 截取的射入时间 tOut: float& -截取的射出时间 pIn: NRPoint2& - 取得截取后的射入点 pOut: NRPoint2& - 取得截取后的射出点 normIn: NRVector2& - 如果射入点在边上,取得射入时击中的边的法向量 normOut: NRVector2& - 如果射出点在边上,取得射出时击中的边的法向量 return value: int - 成功返回1,否则0 */ int NRCyrusBeckClipRadial(NRPoint2 S,NRVector2 c,NRLine2List& L,float& tIn,float& tOut,NRPoint2& pIn,NRPoint2& pOut,NRVector2& normIn,NRVector2& normOut) { float number,denom; float tInTemp = 0.0,tOutTemp=-1.0; NRPoint2 pInTemp,pOutTemp ; NRVector2 normInTemp,normOutTemp ; NRVector2 tmp ; for (int i=0;i<L.num();i++) { //利用公式 tHit=n*(B-A)/(n*c) 其中A和c是目的直线参数表示,B和n是另一条直线点法向量 tmp = L.line[i].pt - S; number = L.line[i].norm * tmp ; denom = L.line[i].norm * c ; if (!NRChopCIRadial(tInTemp,tOutTemp,number,denom,L.line[i].norm,normInTemp,normOutTemp)) { return 0 ; //提前结束 } if (tInTemp>0.0 ) { if(tIn==0.0) { tIn = tInTemp; normIn = normInTemp; } else { tIn=max(tIn,tInTemp); if (tIn == tInTemp) { normIn = normInTemp; } } } if (tOutTemp>=0.0) { if (tOut==-1.0) { tOut = tOutTemp; normOut = normOutTemp ; } else { tOut = min(tOut,tOutTemp); //最早出去的 if (tOut == tOutTemp) { normOut = normOutTemp; } } } } pIn = S+c*tIn; pOut = S+c*tOut; return 1; } //凸多边形截取射线,重载 /* S: NRPoint2 - 射线端点 c: NRVector2 - 射线方向 L: NRLine2List&- 多边形截取区域 pIn: NRPoint2& - 取得截取后的射入点 pOut: NRPoint2& - 取得截取后的射出点 return value: int - 成功返回1,否则0 */ int NRCyrusBeckClipRadial(NRPoint2 S,NRVector2 c,NRLine2List& L,NRPoint2& pIn,NRPoint2& pOut) { float number,denom; float tIn = 0.0,tOut=-1.0; NRVector2 tmp ; for (int i=0;i<L.num();i++) { //利用公式 tHit=n*(B-A)/(n*c) 其中A和c是目的直线参数表示,B和n是另一条直线点法向量 tmp = L.line[i].pt - S; number = L.line[i].norm * tmp ; denom = L.line[i].norm * c ; if (!NRChopCIRadial(tIn,tOut,number,denom)) { return 0 ; //提前结束 } } if (tIn >= 0.0) { pIn=S+c*tIn; } if (tOut >=tIn) { pOut=S+c*tOut; } return 1; } //多个凸多边形截取射线(密室的情况,其它尚未完善) /* S: NRPoint2 - 射线入射端点 c: NRVector2 - 射线方向向量 polygonList: NRPolygonList& - 多边形集 pOut: NRPoint2& - 取得最早的击中点 norm: NRVector2& -取得最早击中点时边的法向量 */ int NRCyrusBeckClipRadial(NRPoint2 S,NRVector2 c,NRPolygonList& polygonList,NRPoint2& pOut ,NRVector2& norm) { float tIn=0.0,tOut=-1.0,tInResult=0.0,tOutResult=-1.0,tHit=0.0; NRPoint2 pInTemp,pInResult,pOutTemp,pOutResult ; NRVector2 normInTemp,normOutTemp; for (int i=0;i<polygonList.num();i++) { tIn=0.0; tOut=-1.0; //取得每个多边形的射入时间和射出时间,取最小值 if(!NRCyrusBeckClipRadial(S,c,polygonList[i],tIn,tOut,pInTemp,pOutTemp,normInTemp,normOutTemp)) { continue; } else //如果射入时间大于0,那么必然是内柱的情况; //如果射出时间大于0,那么必然是墙壁的情况; //还有一种小角度的情况(我只考虑了墙壁) { if (tIn >0.0 && !IsEqual(tIn,0.0)) { if (tHit == 0.0) // 首次 { tHit = tIn; norm = normInTemp; } else { tHit = min(tHit,tIn); //取得最小的射入时间 if (tHit == tIn) { norm = normInTemp; } } } else if (tOut >0.0 && !IsEqual(tOut,0.0)) { if (tHit == 0.0) // 首次 { tHit=tOut; norm = normOutTemp; } else { tHit = min(tOut,tHit); //取得最小的射出时间 if (tHit == tOut) { norm = normOutTemp; } } } else //小角度反射时的处理情况 { if (tOut>0.0 && tHit==0.0) { tHit = tOut; norm = normOutTemp; } } } } pOut = S+c*tHit; return 1 ; } /三角形// //判断是否为三角形 /* A: NRPoint2 - 三角形的一个顶点 B: NRPoint2 - 三角形的一个顶点 C: NRPoint2 - 三角形的一个顶点 return values: int - 成功返回1,否则返回0 */ int NRIsTriangle(NRPoint2 A,NRPoint2 B,NRPoint2 C) { NRVector2 a = B-A; NRVector2 b = C-B ; NRVector2 c = A-C; if (a*b.GetNormalVector() ==0 ) // { return 0 ; } else return 1; } // 取得三角形的内切圆和边的三个交点 /* A: NRPoint2 - 三角形的一个顶点 B: NRPoint2 - 三角形的一个顶点 C: NRPoint2 - 三角形的一个顶点 R: NRPoint2& - 取得AB边与内切圆的交点 S: NRPoint2& - 取得BC边与内切圆的交点 T: NRPoint2& - 取得CA边与内切圆的交点 return value:int - 成功返回1,否则返回0*/ int NRGetTangentPoints(NRPoint2 A,NRPoint2 B,NRPoint2 C,NRPoint2& R,NRPoint2& S,NRPoint2& T) { //the order is important NRVector2 a = B-A; NRVector2 b = C-B ; NRVector2 c = A-C; float absa = a.GetLength(); float absb = b.GetLength(); float absc = c.GetLength(); if (!NRIsTriangle(A,B,C)) { return 0; } float la = (float)((absa + absc-absb)/2.0 ); float lb =(float) ((absa + absb-absc)/2.0); R = A + la*(a/absa); S = B + lb*(b/absb); T = A - la*(c/absc); return 1; } //取得三角形的外接圆 /* A: NRPoint2 - 三角形的一个顶点 B: NRPoint2 - 三角形的一个顶点 C: NRPoint2 - 三角形的一个顶点 S: NRPoint2& - 取得三角形的外接圆圆心 radius: float& - 取得三角形外接圆圆心的半径 return value: int - 成功返回1,否则返回0 */ int NRGetCircumCenter (NRPoint2 A,NRPoint2 B,NRPoint2 C, NRPoint2& S,float& radius) { if (!NRIsTriangle(A,B,C)) { return 0 ; } NRVector2 a,b,c,na; a=B-A; b=C-B; c=A-C; na = a.GetNormalVector(); S=A+ (a+na*((b*c)/(na*c))) / ((float)2.0); float temp = (b*c)/(na*c); radius=(float)((a.GetLength())/2.0 * (sqrt( temp * temp + 1))); return 1; } //分别取得三角形三条边的中点 /* A: NRPoint2 - 三角形的一个顶点 B: NRPoint2 - 三角形的一个顶点 C: NRPoint2 - 三角形的一个顶点 abP: NRPoint2& - 取得AB边的中点 bcP: NRPoint2& - 取得BC边的中点 caP: NRPoint2& - 取得CA边的中点 */ int NRGetCenterPoint(NRPoint2 A,NRPoint2 B,NRPoint2 C,NRPoint2& abP,NRPoint2& bcP, NRPoint2& caP) { if (!NRIsTriangle(A,B,C)) { return 0 ; } abP = (A+B)/2.0; bcP = (B+C)/2.0; caP = (C+A)/2.0; return 1; } //分别取得三角形三条边的垂心 /* A: NRPoint2 - 三角形的一个顶点 B: NRPoint2 - 三角形的一个顶点 C: NRPoint2 - 三角形的一个顶点 abP: NRPoint2 - 取得AB边的垂心 bcP: NRPoint2 - 取得BC边的垂心 caP: NRPoint2 - 取得CA边的垂心 return value: int - 成功返回1,否则0 */ int NRGetPerpendicularPoint(NRPoint2 A, NRPoint2 B,NRPoint2 C,NRPoint2& abP,NRPoint2&bcP,NRPoint2& caP) { //用参数形式表示line CA: C+c*t //用点法向量表示line BP ; n=c; //使用公式 float t = n*(B-A)/(n*c); if(!NRIsTriangle(A,B,C)) { return 0 ; } NRVector2 c; NRVector2 n; float tHit; //calculate abP c=B-A; n=c; tHit = (n * (C-A)) / (n*c); abP = A + c * tHit; //calaculate bcP c=C-B; n=c; tHit = (n * (A-B)) / (n*c); bcP = B + c * tHit; //calculate caP c=A-C; n=c; tHit = (n * (B-C)) / (n*c); caP = C + c * tHit; return 1; } //计算各个端点和垂足连线中点 /* A: NRPoint2 - 三角形的一个顶点 B: NRPoint2 - 三角形的一个顶点 C: NRPoint2 - 三角形的一个顶点 P: NRPoint2 - 垂足 apP: NRPoint2& - 取得A点和垂足P的中点 bpP: NRPoint2& - 取得B点和垂足P的中点 cpP: NRPoint2& - 取得C点和垂足P的中点 */ void NRGetCenterOfPointIntersectPerPendicularPoint(NRPoint2 A,NRPoint2 B,NRPoint2 C,NRPoint2 P,NRPoint2& apP,NRPoint2& bpP,NRPoint2& cpP) { apP = (A+P)/2.0; bpP = (B+P)/2.0; cpP = (C+P)/2.0; } //计算各个端点和垂足连线中点(重载) void NRGetCenterOfPointIntersectPerPendicularPoint(NRPoint2 A,NRPoint2 B,NRPoint2 C,NRPoint2& apP,NRPoint2& bpP,NRPoint2& cpP) { NRPoint2 P ; float radius ; NRGetPerpendicularPoint(A,B,C,apP,bpP,cpP); NRGetCircumCenter(apP,bpP,cpP,P,radius); NRGetCenterOfPointIntersectPerPendicularPoint(A,B,C,P,apP,bpP,cpP); }