直線和圓求交點(向量法)


原文鏈接: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);
        }
    }
} 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM