挖坑自用,可能又不全面的地方欢迎指出
//double已被define为DB
点与向量
点与向量是一个二元组,在运算方面相似,因此可以定义成一个结构体
struct Point {
DB x , y ;
Point ( ) { }
Point ( DB _x , DB _y )
{
x = _x ; y = _y ;
}
Point operator + ( const Point &a ) const
{
return Point ( x + a.x , y + a.y ) ;
}
Point operator - ( const Point &a ) const
{
return Point ( x - a.x , y - a.y ) ;
}
Point operator * ( const DB &a ) const
{
return Point ( x * a , y * a ) ;
}
Point operator / ( const DB &a ) const
{
return Point ( x / a , y / a ) ;
}
DB mo ( )
{
return sqrt ( x * x + y * y ) ;
}
}
其中模长,数乘是针对向量的,加法可以理解为 点+向量=点 或 向量+向量=向量 。减法可以是点-点=向量
基本操作有:点积和叉积,这两个运算都是针对向量的,点积定义为两向量投影的长度之积,公式为x1x2+y1y2,常被用于判垂直(点积为0),叉积 值等于两向量构成的平行四边形的面积,公式为x1y2-y1x2,可用来判断A向量在B向量的顺时针或逆时针方向,若值为0,则重合;若值大于0,则A在B的顺时针;若值小于0,则A在B的逆时针。
DB cha ( Point a , Point b )
{
return a.x * b.y - a.y * b.x ;
}
DB dian ( Point a , Point b )
{
return a.x * b.x + a.y * b.y ;
}
角
对于一个角度,只需记录其sin与cos即可在0-2π中唯一确定,而且用角也是用sin与cos偏多,所以用sin与cos来表示一个角,角相加则直接用公式sin(a+b)=sin(a)cos(b)+cos(a)sin(b)计算,cos亦然(数乘可以用类似快速幂求)。
struct Ang {
DB si , co ;
Ang ( ) { }
Ang ( DB _si , DB _co ) { si = _si ; co = _co ; }
Ang operator + ( const Ang &a ) const
{
return Ang ( si * a.co + co * a.si , co * a.co - si * a.si ) ;
}
Ang operator - ( const Ang &a ) const
{
return Ang ( si * a.co - co * a.si , co * a.co + si * a.si ) ;
}
} ;
操作 : 把一个向量逆时针旋转一个角度。
先取向量的起点为原点,则需要求的是A(x,y)绕原点旋转之后的点坐标,原向量可表示为(kcos(a),ksin(a)),k为A到原点的距离,注意到旋转后A到原点距离不变,只是角度加了一下,所以旋转过后点坐标为(kcos(a+b),ksin(a+b)) 用类似角与角相加的做法即可。我定义的角的第一个元素是sin,所以要换一下。
Point Rota ( Point a , Ang alp ) //逆时针
{
Ang p ( a.y , a.x ) ;
p = p + alp ;
return Point ( p.co , p.si ) ;
}
直线
两点确定一个直线,所以可以用不同的两点来存直线(点+向量亦可)
struct Line {
Point d , e ;
Line ( ) { }
Line ( Point _d , Point _e ) { d = _d ; e = _e ; }
} ;
操作:判断直线是否过一点A。两点到A的向量的叉积为0。
操作:判断两直线位置关系。两直线的向量的叉积为0时平行或重合,否则相交。
操作:求中垂线。先获得一个中点M,再旋转aM向量后相加得到另一个点
操作:两直线求交点。(推荐画图理解)。设a,b是A上两点,c,d是B上两点,交点为e,则向量a->c与a->d的叉积是三角形acd的面积s1*2。同理b->c与b->d的叉积是三角形bcd的面积s2*2,因为acd与bcd同底,再根据三角形相似,s1/s2=ae/be,也就是得到了ae与be长度之比,e=a+(b-a)*(s1 / (s1-s2))。用点与向量计算绕开了分母为0的讨论(唯一情况s1=s2,但这代表a==b,即A的表示不合法),并且因为叉积自带方向性不需讨论正负,比列方程更方便。
Point Line_and_Line ( Line a , Line b )
{
DB s1 = cha ( b.d - a.d , b.e - a.d ) , s2 = cha ( b.d - a.e , b.e - a.e ) ;
return a.d + ( ( a.e - a.d ) * ( s1 / ( s1 - s2 ) ) ) ;
}
圆
圆心加半径存圆
struct Cir {
Point md ;
DB r ;
Cir ( ) { }
Cir ( Point _md , DB _r ) { md = _md ; r = _r ; }
} ;
操作:3点定最小覆盖圆。
先判断3点共线,共线则以
最长的线段为半径做圆。否则求三角形外接圆,就是三角形三边中垂线的交点。
Cir made_2 ( Point a , Point b )
{
Point det = ( b - a ) / 2 ;
return Cir ( a + det , det.mo ( ) ) ;
}
Cir made_3 ( Point a , Point b , Point c )
{
if ( jd ( cha ( a - b , b - c ) ) < eps )
{
if ( b.x > c.x ) swap ( b , c ) ;
if ( a.x > b.x ) swap ( a , b ) ;
if ( b.x > c.x ) swap ( b , c ) ;
return made_2 ( a , c ) ;
}
Point mp = a + ( ( b - a ) / 2 ) , det = a - mp ;
Line g1 ( mp , mp + Rota ( det , Ang ( 1.0 , 0.0 ) ) ) ;
mp = a + ( ( c - a ) / 2 ) , det = a - mp ;
Line g2 ( mp , mp + Rota ( det , Ang ( 1.0 , 0.0 ) ) ) ;
Point _md = Line_and_Line ( g1 , g2 ) ;
return Cir ( _md , dist ( _md , a ) ) ;
}
操作:求圆与直线交点
设求的交点为B,首先可以用圆心+垂直向量获得过圆心与L相垂直的直线,两直线求交得交点A,然后勾股定理求出AB距离,与L的向量的模长做个比即可求出向量AB了。(一般情况有两个对应加和减)
Point Cir_and_Line ( Cir p , Line l )
{
Point vc = l.e - l.d ;
Point vv = Rota ( vc , Ang ( 1.0 , 0.0 ) ) ;
Point another = p.md + vv ;
Point cha = Line_and_Line ( Line ( p.md , another ) , l ) ;
double d = dist ( cha , p.md ) ;
double leth = sqrt ( p.r * p.r - d * d ) ;
double poi = vc.mo ( ) ;
Point xa = cha + vc * leth / poi , xb = cha - vc * leth / poi ;
return xa ;
// xa , xb 即为两交点
}
操作:求过一点圆的切线
设点n与圆的交点为A,则角oAn是直角,求出An的长度,直接旋转即可
Line Point_cut_Cir ( Cir p , Point n )
{
int leth = dist ( p.md , n ) ;
int ntoA = sqrt ( leth * leth - p.r * p.r ) ;
return Rota ( Line ( p.md , n ) , Ang ( p.r / leth , ntoA / leth ) ) ; //顺时针还有一个
}
待更......