1. 程式人生 > >計算幾何--平面向量(1)

計算幾何--平面向量(1)

兩個 images point span tro 射線 幾何 rotate inb

弧度

弧度π=角度180°

坐標表示法(a,b)的含義

大概就是:從(0,0)向(a,b)連一條有向線段,它表示的向量就與向量(a,b)相等。

或者:$(a,b)=ai+bj$,就是正交分解成兩個分別與x、y軸同向的單位向量

點b與點a相減

直接兩組坐標對應減即可,得到向量,得到一個方向為a到b的方向,長度為a與b的直線距離的向量

向量與數的乘/除

直接兩個坐標乘/除該數字即可,得到向量,幾何意義:方向不變,長度乘/除(本文中*號表示數乘)

點與向量的加/減

直接兩組坐標對應加/減即可,得到點,幾何意義:以該點為起點,沿著向量方向移動向量長度

向量與向量的加/減

直接兩組坐標對應加/減即可,得到向量,幾何意義:滿足平行四邊形法則/三角形法則

向量相同

長度相等且方向相同

向量共線(或稱“平行”)

就是方向相同或相反,長度不管。判定:叉積為0

向量a與向量b的內積/點積/數量積(?)(返回一個數量)

a的長度*b的長度*cos夾角,$a{\cdot}b=|a||b|cos\theta$

這裏的夾角指從a到b逆時針旋轉的角,因此夾角絕對值大於90°時點積為負。

技術分享

($cos{\theta}$圖像)

幾何意義:a的長度*b在a的方向上的投影

滿足:

交換律,分配律,結合律(可以幾何證明)

垂直向量的內積為0。

a與b同向,那麽內積等於長度的乘積。

a與b反向,那麽內積等於長度乘積的相反數。

坐標計算:

設$a=x1*i+y1*j,b=x2*i+y2*j$,
那麽$a{\cdot}b=(x1*i+y1*j){\cdot}(x2*i+y2*j)$
$=x1*x2*i^2+x1*y2*i*j+y1*x2*i*j+y1*y2*j^2$
由於i,j垂直且長度為1,那麽$i^2=j^2=1$,$i{\cdot}j=j{\cdot}i=0$
所以$a{\cdot}b=x1*x2+y1*y2$

向量夾角

用內積計算式變換一下即可。

${\theta}=a{\cdot}b/(|a||b|)$

向量長度(也叫“模”)

就是自身內積開平方根。

$|a|=\sqrt{a{\cdot}a}$

也可以理解為勾股定理。

向量a與向量b的叉積/外積(×)(返回一個向量)

(似乎一般只使用這個向量的長度也就是一個數量...)

$a{\times}b=|a||b|sin{\theta}$,就是三角形面積的某個公式乘2

$a{\times}b=a.x*b.y-b.x*a.y$

滿足:

反交換律$a{\times}b=-b{\times}a$

分配律$a{\times}(b+c)=a{\times}b+a{\times}c$

結合律$(r*a){\times}b=a{\times}(r*b)=r(a{\times}b)$

幾何意義:

把a和b的起點固定,a和b組成的三角形的“有向面積”的兩倍,就是形成的一個平行四邊形的面積。

所謂有向面積,如果從a到b旋轉是逆時針,那麽為正,如果是順時針則為負(旋轉取小於等於180°的角)。

或者說,沿著a的方向看,b在左側,那麽為正,如果在右側則為負。

如果a、b共線,不管方向相同還是相反,叉積都為0。

向量旋轉

設rad為逆時針旋轉的角度(弧度制),那麽新向量為$(a.x*cos(rad)-a.y*sin(rad),a.x*sin(rad)+a.y*cos(rad))$

在2-D的迪卡爾坐標系中,一個位置向量的旋轉公式可以由三角函數的幾何意義推出。比如上圖所示是位置向量R逆時針旋轉角度B前後的情況。在左圖中,我們有關系:

  x0 = |R| * cosA

  y0 = |R| * sinA

  =>

  cosA = x0 / |R|

  sinA = y0 / |R|

  在右圖中,我們有關系:

  x1 = |R| * cos(A+B)

  y1 = |R| * sin(A+B)

  其中(x1, y1)就是(x0, y0)旋轉角B後得到的點,也就是位置向量R最後指向的點。我們展開cos(A+B)和sin(A+B),得到

  x1 = |R| * (cosAcosB - sinAsinB)

  y1 = |R| * (sinAcosB + cosAsinB)

  現在把

  cosA = x0 / |R|

  sinA = y0 / |R|

  代入上面的式子,得到

  x1 = |R| * (x0 * cosB / |R| - y0 * sinB / |R|)

  y1 = |R| * (y0 * cosB / |R| + x0 * sinB / |R|)

  =>

  x1 = x0 * cosB - y0 * sinB

  y1 = x0 * sinB + y0 * cosB

  這樣我們就得到了2-D迪卡爾坐標下向量圍繞原點的逆時針旋轉公式。

證明

直線的參數表示

$P+tv$,其中P為點,t為參數,v為向量(表示方向)

直線:t無限制;射線:t大於0;線段:t在0和1之間

直線交點

證明

記錄1 記錄2

設直線分別為$P+tv$和$Q+tw$。交點在第一條直線上參數為t1,第二條直線上參數為t2。

那麽,$P+t1v=Q+t2w$

兩邊同時叉乘w,得到$(P+t1v){\times}w=(Q+t2w){\times}w$

由分配律知,$P{\times}w+t1v{\times}w=Q{\times}w+t2w{\times}w$

由結合律(?)知,$P{\times}w+t1v{\times}w=Q{\times}w+t2(w{\times}w)$

由$w×w=0$知,$P{\times}w+t1v{\times}w=Q{\times}w$

則$t1v{\times}w=Q{\times}w-P{\times}w=(Q-P){\times}w=w{\times}(P-Q)$

$t1=(w{\times}(P-Q))/(v{\times}w)=cross(w,P-Q)/cross(v,w)$

點到直線距離

簡單,畫圖驗證即可。基本就是直線上任取一點,叉積算出某個平行四邊形的面積,再除以底。

直線上有點A、B,P到直線的距離是$(B-A){\times}(P-a)/|(B-A)|$

如果不取絕對值,那麽返回的是有向距離:

//(未驗證)沿直線上兩點形成有向線段方向看去,如果點在右側那麽為負,如果點在左側則為正。

namespace X
{
    const double eps=1e-10;
    struct Point
    {
        double x,y;
        Point(double x=0,double y=0):x(x),y(y){}
    };
    typedef Point Vec;
    Vec operator+(const Vec& a,const Vec& b)
    {
        return Vec(a.x+b.x,a.y+b.y);
    }
    Vec operator-(const Vec& a,const Vec& b)
    {
        return Vec(a.x-b.x,a.y-b.y);
    }
    Vec operator*(const double& a,const Vec& b)
    {
        return Vec(a*b.x,a*b.y);
    }
    Vec operator*(const Vec& a,const double& b)
    {
        return Vec(b*a.x,b*a.y);
    }
    Vec operator/(const Vec& a,const double& b)
    {
        return Vec(a.x/b,a.y/b);
    }
    int dcmp(double x)
    {
        if(fabs(x)<eps)    return 0;
        return x<0?-1:1;
    }
    bool operator==(const Vec& a,const Vec& b)
    {
        return dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0;
    }
    double dot(const Vec& a,const Vec& b)
    {
        return a.x*b.x+a.y*b.y;
    }
    double len(const Vec& x)
    {
        return sqrt(dot(x,x));
    }
    double angle(const Vec& a,const Vec& b)
    {
        return acos(dot(a,b)/len(a)/len(b));
    }
    double cross(const Vec& a,const Vec& b)
    {
        return a.x*b.y-a.y*b.x;
    }
    Vec Rotate(const Vec& a,const double& rad)
    {
        return Vec(a.x*cos(rad)-a.y*sin(rad),a.x*sin(rad)+a.y*cos(rad));
    }
    Point get_line_intersection(const Point& p,const Vec& v,const Point& q,const Vec& w)
    {
        return p+v*cross(w,p-q)/cross(v,w);
    }
    double dis_to_line(const Point& p,const Point& a,const Point& b)
    {
        Vec v1=b-a,v2=p-a;
        return fabs(cross(v1,v2)/len(v1));
    }
    double dis_to_seg(const Point& p,const Point& a,const Point& b)
    {
        if(a==b)    return len(p-a);
        Vec v1=b-a,v2=p-a,v3=p-b;
        if(dcmp(dot(v1,v2))<0)    return len(v2);
        else if(dcmp(dot(v1,v3))>0)    return len(v3);
        else return fabs(cross(v1,v2)/len(v1));
    }
};

計算幾何--平面向量(1)