原文鏈接:https://blog.csdn.net/fengkeyleaf/article/details/117692135
如果看過上一節的直線交點,那么這里求直線和圓的交點思路是非常相似的:
1、用直線到圓心的距離和半徑相比,判斷是否和圓有交點;
2、求出圓心在直線上面的投影點(projectPoint);
3、算出直線的單位向量e;
4、求出一側交點(Intersection)到projectPoint的長度(sideLength);
5、求出sideLength和這側端點到projectPoint距離的比例(ratio);
6、projectPoint +/- ratio * e = 兩側交點;同樣,我們還是通過一個圖例來講解。我們給定圓,圓心為C,半徑為r,和直線AB,以及它們兩個交點E和F:
現在我們以求交點E為例來進行講解。使用求直線交點的思路,如果我們首先會考慮從B出發,求得BE和某個向量的比例,即可通過B來計算交點E,但是這樣不是很好找比例,所以我們換一種思路,先求C在直線AB上面的投影Pr:
那么顯然,我們可以得到一組向量的比例關系:BPr 和 EPr(加粗表示向量),因為圓和直線可能有兩個交點,分別位於Pr的兩邊,所以我們選Pr作為起點求交點比較方便:
有了這個比例關系(ratio),我們只要知道BA的單位向量e,用 ratio * e,就可以得到EPr和PrF。因為根據垂徑定理1,|EPr| = |PrF|。所以我們先假設求得了Pr,那么|EPr|和|BPr|怎么求呢?我們連接BC和EC看看:
從圖中可知,|EPr|不難求,直角三角形CEPr可用勾股定理求得;那么|BPr|呢?現在又需要向量的幫助了,大家還記得向量得點積么?兩個向量a和b的點積表示a在b上面的投影,所以|BPr| = BC * BPr。那么到現在,還剩下兩個問題就能解決交點了:1)如何求C在直線AB上面的投影;2)如何求C到直線AB的距離;
2.2 點在直線上面的投影
在文章開頭,我們曾經提到下面的思路去求解交點:現在我們以求交點E為例來進行講解。使用求直線交點的思路,如果我們首先會考慮從B出發,求得BE和某個向量的比例,即可通過B來計算交點E
雖然這個方法求交點不是很好,但是我們可以用它來求解點在直線上面的投影。還是上面圓的圖例:
所以我們只需要求出BPr和BA的比例關系,即可以求解Pr,而且|BPr|可以BC和BA的點積來求得,因為兩者點積是BC在BA上面的投影,通過這樣,我們就能求得比例關系了,如下圖所示:
2.3 點到直線的距離
點到直線的距離,我們可以看成平行四邊形的高,然后用叉積來求得,還是剛才的圖例:
我們看到C到AB的距離(CPr)其實平行四邊形BADC的高,所以我們可以利用BC和BA的叉積來求解。
代碼來源於https://blog.csdn.net/qq_40998706/article/details/87521165:
這是一個題目:
#include<stdio.h> #include<iostream> #include<cmath> #include<assert.h> using namespace std; #define EPS (1e-10) #define equals(a, b) (fabs((a) - (b)) < EPS) class Point {//Point類,點 public: double x, y; Point(double x = 0, double y = 0): x(x), y(y) {} Point operator + (Point p) { return Point(x + p.x, y + p.y); } Point operator - (Point p) { return Point(x - p.x, y - p.y); } Point operator * (double a) { return Point(a * x, a * y); } Point operator / (double a) { return Point(x / a, y / a); } double abs() { return sqrt(norm()); } double norm() { return x * x + y * y; } bool operator < (const Point &p) const { return x != p.x ? x < p.x : y < p.y; } bool operator == (const Point &p) const { return fabs(x - p.x) < EPS && fabs(y - p.y) < EPS; } }; typedef Point Vector;//Vector類,向量 struct Segment{//Segment 線段 Point p1, p2; }; typedef Segment Line; class Circle {//Circle 圓 public: Point c; double r; Circle(Point c = Point(), double r = 0.0): c(c), r(r) {} }; double dot(Vector a, Vector b) {//內積 return a.x * b.x + a.y * b.y; } double cross(Vector a, Vector b) {//外積 return a.x*b.y - a.y*b.x; } Point project(Segment s, Point p) {//投影 對於給定的三個點p1、p2、p,從點p向通過 //p1、p2的直線引一條垂線,求垂足x的坐標。(點p在直線p1p2上的投影) Vector base = s.p2 - s.p1; double r = dot(p - s.p1, base) / base.norm(); return s.p1 + base * r; } double getDistanceLP(Line l, Point p) {//直線l和點p的距離 return abs(cross(l.p2 - l.p1, p - l.p1) / (l.p2 - l.p1).abs() ); } bool intersect(Circle c, Line l) { if(getDistanceLP(l, c.c) > c.r) { return false; } else { return true; } } Point getCrossPoint(Segment s1, Segment s2) { Vector base = s2.p2 - s2.p1; double d1 = abs(cross(base, s1.p1 - s2.p1)); double d2 = abs(cross(base, s1.p2 - s2.p1)); double t = d1 / (d1 + d2); return s1.p1 + (s1.p2 - s1.p1) * t; } pair<Point, Point> getCrossPoints(Circle c, Line l) { assert(intersect(c, l)); Vector pr = project(l, c.c); Vector e = (l.p2 - l.p1) / (l.p2 - l.p1).abs(); double base = sqrt(c.r * c.r - (pr - c.c).norm() ); return make_pair(pr + e * base, pr - e * base); } int main(){ Circle c; cin>>c.c.x>>c.c.y>>c.r; int q; cin>>q; Line l; pair<Point, Point> p; while(q--){ cin>>l.p1.x>>l.p1.y>>l.p2.x>>l.p2.y; p = getCrossPoints(c, l); if(p.first.x < p.second.x || (p.first.x == p.second.x && p.first.y <= p.second.y) ) { printf("%.6f %.6f %.6f %.6f\n", p.first.x, p.first.y, p.second.x, p.second.y); } else { printf("%.6f %.6f %.6f %.6f\n", p.second.x, p.second.y, p.first.x, p.first.y); } } }