求過圓心直線與圓的兩個交點
阿新 • • 發佈:2018-11-01
主要是注意所使用的資料型別。
之前用的是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; } return line; } /** 已知一點和直線斜率,求該直線的表示式~ */ - (BYLine) getLine:(b2Vec2)point kParam:(double)kParam { BYLine line; line.kExists = true; line.k = kParam; line.b = point.y - kParam * point.x; return line; } - (double) getDistanceBetween2Points:(b2Vec2)p0 anotherPoint:(b2Vec2)p1 { return sqrt(pow(p0.y - p1.y, 2) + pow(p0.x - p1.x, 2)); } /** 獲取一條直線上距離某點一定距離的兩個點~ */ - (b2Vec2*) get2Points:(BYLine)ln p:(b2Vec2)point pw:(double)pathWidth { b2Vec2* target = new b2Vec2[2]; double circleRadius = pathWidth / 2; if(ln.k != 0) { // 斜率存在且不為 0~ double kOfNewLine = -1 / ln.k; BYLine newLine = [self getLine:point kParam:kOfNewLine]; // 經過數學運算,得出二元一次方程組的表示式 double A = pow(newLine.k, 2) + 1; double B = 2 * (newLine.k * newLine.b - newLine.k * point.y - point.x); double C = pow(point.x, 2) + pow((newLine.b - point.y), 2) - pow(circleRadius, 2); double delta = pow(B, 2) - 4 * A * C; if(delta < 0) { // 經實踐檢驗有一定機率走入該分支,必須做特殊化處理~ NSLog(@"竟然會無解,他媽的怎麼回事兒啊!"); target[0] = b2Vec2(point.x, point.y - circleRadius); target[1] = b2Vec2(point.x, point.y + circleRadius); } else { double x1 = (-B + sqrt(delta)) / (2 * A); double y1 = newLine.k * x1 + newLine.b; target[0] = b2Vec2(x1, y1); double x2 = (-B - sqrt(delta)) / (2 * A); double y2 = 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]); return target; } // 繪製觸控點到移動點的軌跡,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); float pathLength = [self getDistanceBetween2Points:pStart anotherPoint:pEnd]; // 設定觸控軌跡的寬度~ float pathWidth = 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 = new b2Vec2[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 部分(部件齊全,能直接拿來跑的)
package org.bruce.vertices.controller.geometry; /** * @author BruceYang * 對點的抽象~ */ public class CGPoint { public double x; public double y; public CGPoint() { } public CGPoint(double x, double y) { this.x = x; this.y = y; } @Override public String toString() { return "x=" + this.x + ", y=" + this.y; } }
package org.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=***, *** 等於多少~
*/
public class CGLine {
// 特別宣告為public型別,免得到時候訪問的時候麻煩,到時候直接點就行了
private boolean kExists; // 大部分情況下 k 都應該是存在的,因此提供一個 true 的預設值~
public double k = 77885.201314f;
public double b = 13145.207788f;
public double extraX = 52077.881314f;
/**
* 這是當 k 存在時的構造方法~
* @param k
* @param b
*/
public CGLine(double k, double b) {
this.kExists = true;
this.k = k;
this.b = b;
}
/**
* 已知兩點,求直線的方程~
* @param p1
* @param p2
*/
public CGLine(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 過該點的直線的斜率
*/
public CGLine(double k, 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
*/
public CGLine(double extraX) {
this.kExists = false;
this.extraX = extraX;
}
@Override
public String toString() {
return "Line.toString()方法被呼叫,y = k*x + b斜截式, k=" + this.k +
", b=" + this.b +
", kExists=" + this.kExists +
", extraX=" + this.extraX;
}
public boolean iskExists() {
return kExists;
}
public void setkExists(boolean kExists) {
this.kExists = kExists;
}
}
---------------------
作者:yang3wei
來源:CSDN
原文:https://blog.csdn.net/yang3wei/article/details/7521298
版權宣告:本文為博主原創文章,轉載請附上博文連結!