java求圆心_求过圆心直线与圆的两个交点(JAVA & OC)

转自:http://blog.csdn.net/yang3wei/article/details/7521298

主要是注意所使用的数据类型。

之前用的是float,出现了一些意外,而且花费了我不少时间来反复验证、推导,

做了很多的无用功,而且,反复推导得出来的计算步骤并没有什么不牢靠的地方。

然后计算得到的结果却是让人如此之不省心,梗的我闷得慌。

今天上午发来了一贴,多位朋友各抒己见,

总算是让我发现了一些不足的地方,首当其冲的是一个变量弄错了,

导致大批的计算失准。

后来修正了这个bug以后,还是会出现计算不精确的地方。

再后来便将涉及的所有成员变量由float 纠正为 double 类型,

计算精度果然得到了提高,失准的地方再次被干掉。

这次给自己的教训就是:

涉及到精度比较高的数值运算的时候,还是得统统用 double。

之前还以为 float 已经比较不错,能够满足基本的需求了,

经过这次我总算是懂了,double的存在离我并不遥远。

这个问题堵了我比较久了,大概也有快10个月了,当时没解决就规避之没去用了,

今天能够解决这个遗留已久的问题,真是让人心情愉快!

下面贴出 Objective-C 和 Java 的相关代码:

Objective-C 部分(核心代码摘录)

/** 已知两点,求过该两点的直线表达式~ */

- (BYLine) getLine:(b2Vec2)p1 anotherPoint:(b2Vec2)p2 {

BYLine line;

if((p1.x - p2.x) != 0) {

line.kExists = true;

line.k = (p1.y - p2.y) / (p1.x - p2.x);

line.b = p1.y - line.k * p1.x;

} else{

line.kExists = false;

line.extraX = p1.x;

}

returnline;

}

/** 已知一点和直线斜率,求该直线的表达式~ */

- (BYLine) getLine:(b2Vec2)point kParam:(double)kParam {

BYLine line;

line.kExists = true;

line.k = kParam;

line.b = point.y - kParam * point.x;

returnline;

}

- (double) getDistanceBetween2Points:(b2Vec2)p0 anotherPoint:(b2Vec2)p1 {

returnsqrt(pow(p0.y - p1.y, 2) + pow(p0.x - p1.x, 2));

}

/** 获取一条直线上距离某点一定距离的两个点~ */

- (b2Vec2*) get2Points:(BYLine)ln p:(b2Vec2)point pw:(double)pathWidth {

b2Vec2* target = newb2Vec2[2];

doublecircleRadius = pathWidth / 2;

if(ln.k != 0) {

// 斜率存在且不为 0~

doublekOfNewLine = -1 / ln.k;

BYLine newLine = [self getLine:point kParam:kOfNewLine];

// 经过数学运算,得出二元一次方程组的表达式

doubleA = pow(newLine.k, 2) + 1;

doubleB = 2 * (newLine.k * newLine.b - newLine.k * point.y - point.x);

doubleC = pow(point.x, 2) + pow((newLine.b - point.y), 2) - pow(circleRadius, 2);

doubledelta = pow(B, 2) - 4 * A * C;

if(delta 

NSLog(@"竟然会无解,他妈的怎么回事儿啊!");

target[0] = b2Vec2(point.x, point.y - circleRadius);

target[1] = b2Vec2(point.x, point.y + circleRadius);

} else{

doublex1 = (-B + sqrt(delta)) / (2 * A);

doubley1 = newLine.k * x1 + newLine.b;

target[0] = b2Vec2(x1, y1);

doublex2 = (-B - sqrt(delta)) / (2 * A);

doubley2 = newLine.k * x2 + newLine.b;

target[1] = b2Vec2(x2, y2);

}

} else{

// 斜率存在且为 0~

target[0] = b2Vec2(point.x, point.y - circleRadius);

target[1] = b2Vec2(point.x, point.y + circleRadius);

}

NSLog(@"离中心点的距离为:%f", [self getDistanceBetween2Points:target[0] anotherPoint:point]);

returntarget;

}

// 绘制触摸点到移动点的轨迹,1个像素~

- (void) drawTouchPath {

if(_mouseDown) {

// 已知(2等分,用分数表示~)

b2Vec2 pStart = _touchSegment.p1;

b2Vec2 pEnd = _touchSegment.p2;

// 推出

b2Vec2 pMiddle = b2Vec2((pStart.x + pEnd.x) / 2, (pStart.y + pEnd.y) / 2);

floatpathLength = [self getDistanceBetween2Points:pStart anotherPoint:pEnd];

// 设置触摸轨迹的宽度~

floatpathWidth = pathLength / 3.0f;

if(pathWidth > TOUCH_PATH_MAX_WIDTH) {

pathWidth = TOUCH_PATH_MAX_WIDTH;

}

b2Vec2* result;

BYLine expFunc = [self getLine:pStart anotherPoint:pEnd];

if(expFunc.kExists) {// 斜率存在~

result = [self get2Points:expFunc p:pMiddle pw:pathWidth];

} else{// 斜率不存在~

result = newb2Vec2[2];

result[0] = b2Vec2(pMiddle.x - pathWidth / 2, pMiddle.y);

result[1] = b2Vec2(pMiddle.x + pathWidth / 2, pMiddle.y);

}

b2Vec2 finalResult[5];

finalResult[0] = pStart;

finalResult[1] = result[0];

finalResult[2] = pEnd;

finalResult[3] = result[1];

finalResult[4] = pStart;

// 绘制白色内容物~

glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

glVertexPointer(2, GL_FLOAT, 0, finalResult);

glDrawArrays(GL_TRIANGLE_STRIP, 0, 5);

}

}

Java 部分(部件齐全,能直接拿来跑的)

packageorg.bruce.vertices.controller.geometry;

/**

* @author BruceYang

* 对点的抽象~

*/

publicclassCGPoint {

publicdoublex;

publicdoubley;

publicCGPoint() {

}

publicCGPoint(doublex,doubley) {

this.x = x;

this.y = y;

}

@Override

publicString toString() {

return"x="+this.x +", y="+this.y;

}

}

*****************************************************

packageorg.bruce.vertices.controller.geometry;

/**

* @author BruceYang

* 这个是对通用一次直线方程 A*x + B*y + C = 0 的封装~

* 本来封装的是斜截式,不过发现当斜率k不存在的时候会比较麻烦,因此该用一般式

* 再个就是接着用一般式的演变方式 x + B/A*y + C/A = 0,但是考虑到可能存在x == 0 的情况,因此又舍弃~

*

* 娘的,一般式还是他妈的无济于事啊,改回斜截式,多提供两个成员变量:

* 一个boolean表示k是否存在,一个额外的float表示k不存在的时候直线方程 x=***, *** 等于多少~

*/

publicclassCGLine {

// 特别声明为public类型,免得到时候访问的时候麻烦,到时候直接点就行了

privatebooleankExists;// 大部分情况下 k 都应该是存在的,因此提供一个 true 的默认值~

publicdoublek =77885.201314f;

publicdoubleb =13145.207788f;

publicdoubleextraX =52077.881314f;

/**

* 这是当 k 存在时的构造方法~

* @param k

* @param b

*/

publicCGLine(doublek,doubleb) {

this.kExists =true;

this.k = k;

this.b = b;

}

/**

* 已知两点,求直线的方程~

* @param p1

* @param p2

*/

publicCGLine(CGPoint p1, CGPoint p2) {

if((p1.x - p2.x) !=0) {

CGDbg.println("y = k*x + b, k exits!!");

this.kExists =true;

this.k = (p1.y - p2.y)/(p1.x - p2.x);

this.b = (p1.y - p1.x * k);

} else{

CGDbg.println("y = k*x + b, k doesn't exists!!");

// 如果走进这个分支,表示直线垂直于x轴,斜率不存在,保留k的默认值~

this.kExists =false;

this.extraX = p1.x;

}

CGDbg.print("过p1("+p1.x+", "+p1.y +"), p2("+p2.x+", "+p2.y+")两点的直线方程表达式为: ");

if(kExists) {

CGDbg.println("y = "+ k +"*x + "+ b);

} else{

CGDbg.println("x = "+ extraX +"(垂直于x轴!)");

}

}

/**

* 点斜式~

* @param p 某点

* @param k 过该点的直线的斜率

*/

publicCGLine(doublek, CGPoint p) {

/**

* (y-y') = k*(x-x')

* 变形成斜截式为:

* y = k*x + y' - k*x'

* k = k, b = y'-k*x'

*/

this.kExists =true;

this.k = k;

this.b = p.y - k * p.x;

}

/**

* 这是当 k 不存在时的构造方法~

* @param extraX

*/

publicCGLine(doubleextraX) {

this.kExists =false;

this.extraX = extraX;

}

@Override

publicString toString() {

return"Line.toString()方法被调用,y = k*x + b斜截式, k="+this.k +

", b="+this.b +

", kExists="+this.kExists +

", extraX="+this.extraX;

}

publicbooleaniskExists() {

returnkExists;

}

publicvoidsetkExists(booleankExists) {

this.kExists = kExists;

}

}

*****************************************************

packageorg.bruce.vertices.controller.geometry;

/**

* @author Bruce Yang

* 用于打印调试~

*/

publicclassCGDbg {

publicstaticfinalbooleanDEBUG_MODE =true;

// 方便进行调试信息的输出,开关~

publicstaticvoidprintln(Object info) {

if(DEBUG_MODE) {

System.out.println(info);

}

}

publicstaticvoidprint(Object info) {

if(DEBUG_MODE) {

System.out.print(info);

}

}

}

*****************************************************

packageorg.bruce.vertices.controller.geometry;

/**

* @author BruceYang

*/

publicclassCGGeometryLib {

/**

* @param p0    第一个点的坐标

* @param p1    第二个点的坐标

* @return      两个点之间的距离

* 计算出两点之间的距离

*/

publicstaticdoublegetDistanceBetween2Points(CGPoint p0, CGPoint p1) {

doubledistance = Math.sqrt(Math.pow(p0.y - p1.y,2) + Math.pow(p0.x - p1.x,2));

returndistance;

}

/**

* @param p

* @param l

* @return      该方法用于获取某点在某条直线上的投影点的坐标

*/

publicstaticCGPoint getProjectivePoint(CGPoint p, CGLine l) {

CGPoint target = null;

if(l.iskExists()) {

if(l.k !=0) {

CGLine newLine = newCGLine(-1/l.k, p.y -(-1/l.k)*p.x);

target = getCrossPoint(l, newLine);

} else{// 如果直线l的斜率存在且斜率的值为0,明显是一条平行于x轴的直线~

// 此时,点 p 到直线 l 的距离为:Math.abs(p.y-l.b)

target = newCGPoint(p.x, l.b);

}

} else{// 如果直线l的斜率不存在,明显是一条垂直于x轴的直线~

// 此时,点 p 到直线 l 的距离为:Math.abs(p.x-l.extraX)

target = newCGPoint(l.extraX, p.y);

}

CGDbg.println("点 ("+p.x+", "+p.y+") 在直线:y="+l.k+"x+"+l.b+" 上的投影点为 ("+target.x+", "+target.y+")");

returntarget;

}

/**

* 该方法用于求出两条直线的交点坐标

* 这个方法是定制的,只有 l1, l2 均存在斜率 k 时方能使用(限制取消)~

* @param l1

* @param l2

* @return

*/

publicstaticCGPoint getCrossPoint(CGLine l1, CGLine l2) {

//      dbgPrintln("into the getCrossPoint, l1: " + l1);

//      dbgPrintln("into the getCrossPoint, l2: " + l2);

doublex, y;

if(l1.iskExists() && l2.iskExists()) {

x = (l2.b - l1.b) / (l1.k - l2.k);

y = l1.k * x + l1.b;

} elseif(!l1.iskExists() && l2.iskExists()) {

x = l1.extraX;

y = l2.k * x + l2.b;

} elseif(l1.iskExists() && !l2.iskExists()) {

x = l2.extraX;

y = l1.k * x + l1.b;

} else{

// 两条直线的斜率都不存在?!,不可能发生的情况!!

x = 0;

y = 0;

}

CGDbg.println("getCrossPoint, CGPoint(x="+ x +", y="+ y +")");

returnnewCGPoint(x, y);

}

/**

* @param args

* 怎样判断是否符合要求?将过每组3个点中除开多边形顶点的两个点的直线方程求出来

* 比较求出的 4 个 候选圆心点 哪个与此直线离的比较近,哪个就是符合要求的圆心点

* 以下方法用于获取离特定直线距离最近的一个点(目前只支持斜率k存在的直线,以后慢慢扩充)!

* 要得到距离特定直线距离最远的一个点只要稍作改动即可!

*/

publicstaticCGPoint getNearestPoint(CGPoint[] points, CGLine line) {

doubleminDistance =0;

intminIndex =0;

if(line.iskExists()) {

// 直线斜率存在的分支~

for(inti =0; i 

CGPoint p = points[i];

doubled = Math.abs(line.k*p.x-p.y+line.b)/Math.sqrt(Math.pow(line.k,2)+1);

if(i ==0) {

// 赋予初值,不然 minDistance 的值就为 0 了~

minDistance = d;

}

if(d 

minDistance = d;

minIndex = i;

}

}

} else{

// 直线斜率不存在的分支(亦即直线垂直于 x 轴)~

for(inti =0; i 

CGPoint p = points[i];

doubled = Math.abs(p.x - line.extraX);

if(i ==0) {

// 赋予初值,不然minDistance的值就为0了~

minDistance = d;

}

if(d 

minDistance = d;

minIndex = i;

}

}

}

CGPoint dest = points[minIndex];

CGDbg.println("即将离开chooseOne()方法,圆心点为:("+dest.x+", "+dest.y+")");

returndest;

}

/**

* 获取传入两点的中点~

* @param p1

* @param p2

* @return

*/

publicstaticCGPoint getMiddlePoint(CGPoint p1, CGPoint p2) {

returnnewCGPoint((p1.x + p2.x) /2.0f, (p1.y + p2.y) /2.0f);

}

/**

* 封装一下 Math 的 pow 、sqrt 方法,调用起来方便一些~

* @param d1

* @param d2

* @return

*/

publicstaticdoublepow(doubled1,doubled2) {

returnMath.pow(d1, d2);

}

publicstaticdoublesqrt(doubled) {

returnMath.sqrt(d);

}

publicstaticdoublesin(doubletheta) {

returnMath.sin(theta);

}

publicstaticdoublecos(doubletheta) {

returnMath.cos(theta);

}

/**

* 传入线段的两个端点,获取中点,以该中点为圆心做半径为 radius 的圆,

* 经过线段中点做线段的垂线,返回垂线与圆的两个交点~

* Objective-C 里面的结果有点儿问题,不知道是什么原因,来java 里面碰碰有运气~

* @param p1        线段端点1

* @param p2        线段端点2

* @param radius    圆半径

* @return          线段中垂线与圆的两个交点~

*/

publicstaticCGPoint[] getWhatIWanted(CGPoint p1, CGPoint p2,doubleradius) {

CGPoint[] target = newCGPoint[2];

CGPoint pMiddle = getMiddlePoint(p1, p2);

//      double segLength = getDistanceBetween2Points(p1, p2);

CGLine l1 = newCGLine(p1, p2);

if(l1.iskExists()) {

if(l1.k !=0) {

doublekOfNewLine = -1/ l1.k;

CGLine newLine = newCGLine(kOfNewLine, pMiddle);

// 经过数学运算,得出二元一次方程组的表达式

doubleA = pow(newLine.k,2) +1;

doubleB =2* (newLine.k * newLine.b - newLine.k * pMiddle.y - pMiddle.x);

doubleC = pow(pMiddle.x,2) + pow((newLine.b - pMiddle.y),2) - pow(radius,2);

doubledelta = pow(B,2) -4* A * C;

if(delta <0) {// 经实践检验有一定几率走入该分支,必须做特殊化处理~

// 2012。04。28。20。01,精度不够所致,换成double后无该情况出现~

CGDbg.println("竟然会无解,他妈的怎么回事儿啊!");

target[0] =newCGPoint(pMiddle.x, pMiddle.y - radius);

target[1] =newCGPoint(pMiddle.x, pMiddle.y + radius);

} else{

doublex1 = (-B + sqrt(delta)) / (2* A);

doubley1 = newLine.k * x1 + newLine.b;

target[0] =newCGPoint(x1, y1);

doublex2 = (-B - sqrt(delta)) / (2* A);

doubley2 = newLine.k * x2 + newLine.b;

target[1] =newCGPoint(x2, y2);

}

} else{

target[0] =newCGPoint(pMiddle.x, pMiddle.y - radius);

target[1] =newCGPoint(pMiddle.x, pMiddle.y + radius);

}

} else{

target[0] =newCGPoint(pMiddle.x - radius, pMiddle.y);

target[1] =newCGPoint(pMiddle.x + radius, pMiddle.y);

}

System.out.println("target[0] 距离中点的距离为:"+ getDistanceBetween2Points(target[0], pMiddle));

System.out.println("target[1] 距离中点的距离为:"+ getDistanceBetween2Points(target[1], pMiddle));

returntarget;

}

/**

* 测试实用性,测试结果如下:

* 之前用 float 类型的时候,每隔 1 度测试一次,共测试一个圆周,无解的情况出现一次

* 每隔 1 度测试一次, 共测试一个圆周,无解的情况无。

* 每隔 0.5 度测试一次,共测试一个圆周,无解的情况只出现一次

* @param args

*/

publicstaticvoidmain(String[] args) {

doublecurrentRadian =0;

doubledeltaRadian = Math.PI /360;

doublebigRadius =50;

doublesmallRadius =20;

CGPoint origin = newCGPoint(0,0);// 原点~

CGPoint tail = null;// tail 是尾巴、末梢的意思~

for(inti =0; i <720; ++ i) {

System.out.println(" -- 第 "+ i +"度!");

tail = newCGPoint(bigRadius*cos(currentRadian), bigRadius*sin(currentRadian));

currentRadian += deltaRadian;

getWhatIWanted(origin, tail, smallRadius);

}

}

}

首先,我们可以计算出三角形ABC的外接圆心O和半径R。 以线段AB为直径的圆心为线段中点M,即M=(-1.5, 1.5)。由于圆心O在直线MC的中垂线上,因此可以直线MC的斜率为-4/3。同理,可以直线MB的斜率为-3/2。再利用直线的斜截式方程,可以写出直线MC和MB的方程: MC: y = (-4/3)x + 14/3 MB: y = (-3/2)x + 5 解这两个方程,得到它们的交点坐标为O=(1,3)。此时,我们可以计算出的半径R=OA=OB=OC。 接下来,我们需要出点P到直线l的距离。由于直线l的一般式方程为3x+4y-40=0,因此可以将点P的坐标代入该方程,出点P到直线l的距离d: d = |3p + 4q - 40| / 5 其中,点P的坐标为(p,q)。为了方便计算,我们可以将直线l的一般式方程转化为点斜式方程: y = (-3/4)x + 10 这样,点P到直线l的距离可以表示为: d = |(-3/4)p + q + 10| / (5/4) 为了出d的取值范围,我们可以将点P的坐标表示为圆心O和半径R的函数: (p-1)^2 + (q-3)^2 = R^2 展开得到: p^2 + q^2 - 2p - 6q + 9 = R^2 - 1 将此式中的p代入上面的d的式子,得到: d = |(-3/4)p + (R^2-1)/5 + 10/5| / (5/4) 整理后得到: d = |(-3/4)p + (R^2+1)/5| / (5/4) 注意到该式中只有p是变量,因此我们可以将其视为一条直线的方程,而|R^2+1| / 5是一个常数。因此,点P到直线l的距离的取值范围就是以直线(-3/4)p + (R^2+1)/5 = 0为边界的两侧区域。这条直线的斜率为-4/3,因此它与直线MC的斜率相等。同时,圆心O的纵坐标为3,因此它的截距也可以出来。将它们代入直线的斜截式方程,得到: (-3/4)p + (R^2+1)/5 = -4/3*(p-1) + 3 整理得到: p = (4R^2-1) / 25 因此,点P到直线l的距离的取值范围是: d = |(-3/4)(4R^2-1)/25 + (R^2+1)/5| / (5/4) 化简得到: d = |R^2-9| / 25 因此,d的取值范围是[0,2/5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值