計算幾何經典模板
/**
* 二維ACM計算幾何模板
* 注意變量類型更改和EPS
* #include <cmath>
* #include <cstdio>
* By OWenT
*/
const double eps = 1e-8;
const double pi = std::acos(-1.0);
//點
class point
{
public:
double x, y;
point(){};
point(double x, double y):x(x),y(y){};
static int xmult(const point &ps, const point &pe, const point &po)
{
return (ps.x - po.x) * (pe.y - po.y) - (pe.x - po.x) * (ps.y - po.y);
}
//相對原點的差乘結果,參數:點[_Off]
//即由原點和這兩個點組成的平行四邊形面積
double operator *(const point &_Off) const
{
return x * _Off.y - y * _Off.x;
}
//相對偏移
point operator - (const point &_Off) const
{
return point(x - _Off.x, y - _Off.y);
}
//點位置相同(double類型)
bool operator == (const point &_Off) const
{
return std::fabs(_Off.x - x) < eps && std::fabs(_Off.y - y) < eps;
}
//點位置不同(double類型)
bool operator != (const point &_Off) const
{
return ((*this) == _Off) == false;
}
//兩點間距離的平方
double dis2(const point &_Off) const
{
return (x - _Off.x) * (x - _Off.x) + (y - _Off.y) * (y - _Off.y);
}
//兩點間距離
double dis(const point &_Off) const
{
return std::sqrt((x - _Off.x) * (x - _Off.x) + (y - _Off.y) * (y - _Off.y));
}
};
//兩點表示的向量
class pVector
{
public:
point s, e;//兩點表示,起點[s],終點[e]
double a, b, c;//一般式,ax+by+c=0
pVector(){}
pVector(const point &s, const point &e):s(s),e(e){}
//向量與點的叉乘,參數:點[_Off]
//[點相對向量位置判斷]
double operator *(const point &_Off) const
{
return (_Off.y - s.y) * (e.x - s.x) - (_Off.x - s.x) * (e.y - s.y);
}
//向量與向量的叉乘,參數:向量[_Off]
double operator *(const pVector &_Off) const
{
return (e.x - s.x) * (_Off.e.y - _Off.s.y) - (e.y - s.y) * (_Off.e.x - _Off.s.x);
}
//從兩點表示轉換爲一般表示
bool pton()
{
a = s.y - e.y;
b = e.x - s.x;
c = s.x * e.y - s.y * e.x;
return true;
}
//-----------點和直線(向量)-----------
//點在向量左邊(右邊的小於號改成大於號即可,在對應直線上則加上=號)
//參數:點[_Off],向量[_Ori]
friend bool operator<(const point &_Off, const pVector &_Ori)
{
return (_Ori.e.y - _Ori.s.y) * (_Off.x - _Ori.s.x)
< (_Off.y - _Ori.s.y) * (_Ori.e.x - _Ori.s.x);
}
//點在直線上,參數:點[_Off]
bool lhas(const point &_Off) const
{
return std::fabs((*this) * _Off) < eps;
}
//點在線段上,參數:點[_Off]
bool shas(const point &_Off) const
{
return lhas(_Off)
&& _Off.x - std::min(s.x, e.x) > -eps && _Off.x - std::max(s.x, e.x) < eps
&& _Off.y - std::min(s.y, e.y) > -eps && _Off.y - std::max(s.y, e.y) < eps;
}
//點到直線/線段的距離
//參數: 點[_Off], 是否是線段[isSegment](默認爲直線)
double dis(const point &_Off, bool isSegment = false)
{
//化爲一般式
pton();
//到直線垂足的距離
double td = (a * _Off.x + b * _Off.y + c) / sqrt(a * a + b * b);
//如果是線段判斷垂足
if(isSegment)
{
double xp = (b * b * _Off.x - a * b * _Off.y - a * c) / ( a * a + b * b);
double yp = (-a * b * _Off.x + a * a * _Off.y - b * c) / (a * a + b * b);
double xb = std::max(s.x, e.x);
double yb = std::max(s.y, e.y);
double xs = s.x + e.x - xb;
double ys = s.y + e.y - yb;
if(xp > xb + eps || xp < xs - eps || yp > yb + eps || yp < ys - eps)
td = std::min(_Off.dis(s), _Off.dis(e));
}
return fabs(td);
}
//關於直線對稱的點
point mirror(const point &_Off) const
{
//注意先轉爲一般式
point ret;
double d = a * a + b * b;
ret.x = (b * b * _Off.x - a * a * _Off.x - 2 * a * b * _Off.y - 2 * a * c) / d;
ret.y = (a * a * _Off.y - b * b * _Off.y - 2 * a * b * _Off.x - 2 * b * c) / d;
return ret;
}
//計算兩點的中垂線
static pVector ppline(const point &_a, const point &_b)
{
pVector ret;
ret.s.x = (_a.x + _b.x) / 2;
ret.s.y = (_a.y + _b.y) / 2;
//一般式
ret.a = _b.x - _a.x;
ret.b = _b.y - _a.y;
ret.c = (_a.y - _b.y) * ret.s.y + (_a.x - _b.x) * ret.s.x;
//兩點式
if(std::fabs(ret.a) > eps)
{
ret.e.y = 0.0;
ret.e.x = - ret.c / ret.a;
if(ret.e == ret. s)
{
ret.e.y = 1e10;
ret.e.x = - (ret.c - ret.b * ret.e.y) / ret.a;
}
}
else
{
ret.e.x = 0.0;
ret.e.y = - ret.c / ret.b;
if(ret.e == ret. s)
{
ret.e.x = 1e10;
ret.e.y = - (ret.c - ret.a * ret.e.x) / ret.b;
}
}
return ret;
}
//------------直線和直線(向量)-------------
//直線重合,參數:直線向量[_Off]
bool equal(const pVector &_Off) const
{
return lhas(_Off.e) && lhas(_Off.s);
}
//直線平行,參數:直線向量[_Off]
bool parallel(const pVector &_Off) const
{
return std::fabs((*this) * _Off) < eps;
}
//兩直線交點,參數:目標直線[_Off]
point crossLPt(pVector _Off)
{
//注意先判斷平行和重合
point ret = s;
double t = ((s.x - _Off.s.x) * (_Off.s.y - _Off.e.y) - (s.y - _Off.s.y) * (_Off.s.x - _Off.e.x))
/ ((s.x - e.x) * (_Off.s.y - _Off.e.y) - (s.y - e.y) * (_Off.s.x - _Off.e.x));
ret.x += (e.x - s.x) * t;
ret.y += (e.y - s.y) * t;
return ret;
}
//------------線段和直線(向量)----------
//線段和直線交
//參數:線段[_Off]
bool crossSL(const pVector &_Off) const
{
double rs = (*this) * _Off.s;
double re = (*this) * _Off.e;
return rs * re < eps;
}
//------------線段和線段(向量)----------
//判斷線段是否相交(注意添加eps),參數:線段[_Off]
bool isCrossSS(const pVector &_Off) const
{
//1.快速排斥試驗判斷以兩條線段爲對角線的兩個矩形是否相交
//2.跨立試驗(等於0時端點重合)
return (
(std::max(s.x, e.x) >= std::min(_Off.s.x, _Off.e.x)) &&
(std::max(_Off.s.x, _Off.e.x) >= std::min(s.x, e.x)) &&
(std::max(s.y, e.y) >= std::min(_Off.s.y, _Off.e.y)) &&
(std::max(_Off.s.y, _Off.e.y) >= std::min(s.y, e.y)) &&
((pVector(_Off.s, s) * _Off) * (_Off * pVector(_Off.s, e)) >= 0.0) &&
((pVector(s, _Off.s) * (*this)) * ((*this) * pVector(s, _Off.e)) >= 0.0)
);
}
};
class polygon
{
public:
const static long maxpn = 100;
point pt[maxpn];//點(順時針或逆時針)
long n;//點的個數
point& operator[](int _p)
{
return pt[_p];
}
//求多邊形面積,多邊形內點必須順時針或逆時針
double area() const
{
double ans = 0.0;
int i;
for(i = 0; i < n; i ++)
{
int nt = (i + 1) % n;
ans += pt[i].x * pt[nt].y - pt[nt].x * pt[i].y;
}
return std::fabs(ans / 2.0);
}
//求多邊形重心,多邊形內點必須順時針或逆時針
point gravity() const
{
point ans;
ans.x = ans.y = 0.0;
int i;
double area = 0.0;
for(i = 0; i < n; i ++)
{
int nt = (i + 1) % n;
double tp = pt[i].x * pt[nt].y - pt[nt].x * pt[i].y;
area += tp;
ans.x += tp * (pt[i].x + pt[nt].x);
ans.y += tp * (pt[i].y + pt[nt].y);
}
ans.x /= 3 * area;
ans.y /= 3 * area;
return ans;
}
//判斷點在凸多邊形內,參數:點[_Off]
bool chas(const point &_Off) const
{
double tp = 0, np;
int i;
for(i = 0; i < n; i ++)
{
np = pVector(pt[i], pt[(i + 1) % n]) * _Off;
if(tp * np < -eps)
return false;
tp = (std::fabs(np) > eps)?np: tp;
}
return true;
}
//判斷點是否在任意多邊形內[射線法],O(n)
bool ahas(const point &_Off) const
{
int ret = 0;
double infv = 1e-10;//座標系最大範圍
pVector l = pVector(_Off, point( -infv ,_Off.y));
for(int i = 0; i < n; i ++)
{
pVector ln = pVector(pt[i], pt[(i + 1) % n]);
if(fabs(ln.s.y - ln.e.y) > eps)
{
point tp = (ln.s.y > ln.e.y)? ln.s: ln.e;
if(fabs(tp.y - _Off.y) < eps && tp.x < _Off.x + eps)
ret ++;
}
else if(ln.isCrossSS(l))
ret ++;
}
return (ret % 2 == 1);
}
//凸多邊形被直線分割,參數:直線[_Off]
polygon split(pVector _Off)
{
//注意確保多邊形能被分割
polygon ret;
point spt[2];
double tp = 0.0, np;
bool flag = true;
int i, pn = 0, spn = 0;
for(i = 0; i < n; i ++)
{
if(flag)
pt[pn ++] = pt[i];
else
ret.pt[ret.n ++] = pt[i];
np = _Off * pt[(i + 1) % n];
if(tp * np < -eps)
{
flag = !flag;
spt[spn ++] = _Off.crossLPt(pVector(pt[i], pt[(i + 1) % n]));
}
tp = (std::fabs(np) > eps)?np: tp;
}
ret.pt[ret.n ++] = spt[0];
ret.pt[ret.n ++] = spt[1];
n = pn;
return ret;
}
//-------------凸包-------------
//Graham掃描法,複雜度O(nlg(n)),結果爲逆時針
//#include <algorithm>
static bool graham_cmp(const point &l, const point &r)//凸包排序函數
{
return l.y < r.y || (l.y == r.y && l.x < r.x);
}
polygon& graham(point _p[], int _n)
{
int i, len;
std::sort(_p, _p + _n, polygon::graham_cmp);
n = 1;
pt[0] = _p[0], pt[1] = _p[1];
for(i = 2; i < _n; i ++)
{
while(n && point::xmult(_p[i], pt[n], pt[n - 1]) >= 0)
n --;
pt[++ n] = _p[i];
}
len = n;
pt[++ n] = _p[_n - 2];
for(i = _n - 3; i >= 0; i --)
{
while(n != len && point::xmult(_p[i], pt[n], pt[n - 1]) >= 0)
n --;
pt[++ n] = _p[i];
}
return (*this);
}
//凸包旋轉卡殼(注意點必須順時針或逆時針排列)
//返回值凸包直徑的平方(最遠兩點距離的平方)
double rotating_calipers()
{
int i = 1;
double ret = 0.0;
pt[n] = pt[0];
for(int j = 0; j < n; j ++)
{
while(fabs(point::xmult(pt[j], pt[j + 1], pt[i + 1])) > fabs(point::xmult(pt[j], pt[j + 1], pt[i])) + eps)
i = (i + 1) % n;
//pt[i]和pt[j],pt[i + 1]和pt[j + 1]可能是對踵點
ret = std::max(ret, std::max(pt[i].dis(pt[j]), pt[i + 1].dis(pt[j + 1])));
}
return ret;
}
//凸包旋轉卡殼(注意點必須逆時針排列)
//返回值兩凸包的最短距離
double rotating_calipers(polygon &_Off)
{
int i = 0;
double ret = 1e10;//inf
pt[n] = pt[0];
_Off.pt[_Off.n] = _Off.pt[0];
//注意凸包必須逆時針排列且pt[0]是左下角點的位置
while(_Off.pt[i + 1].y > _Off.pt[i].y)
i = (i + 1) % _Off.n;
for(int j = 0; j < n; j ++)
{
double tp;
//逆時針時爲 >,順時針則相反
while((tp = point::xmult(pt[j], pt[j + 1], _Off.pt[i + 1]) - point::xmult( pt[j], pt[j + 1], _Off.pt[i])) > eps)
i = (i + 1) % _Off.n;
//(pt[i],pt[i+1])和(_Off.pt[j],_Off.pt[j + 1])可能是最近線段
ret = std::min(ret, pVector(pt[j], pt[j + 1]).dis(_Off.pt[i], true));
ret = std::min(ret, pVector(_Off.pt[i], _Off.pt[i + 1]).dis(pt[j + 1], true));
if(tp > -eps)//如果不考慮TLE問題最好不要加這個判斷
{
ret = std::min(ret, pVector(pt[j], pt[j + 1]).dis(_Off.pt[i + 1], true));
ret = std::min(ret, pVector(_Off.pt[i], _Off.pt[i + 1]).dis(pt[j], true));
}
}
return ret;
}
//-----------半平面交-------------
//複雜度:O(nlog2(n))
//#include <algorithm>
//半平面計算極角函數[如果考慮效率可以用成員變量記錄]
static double hpc_pa(const pVector &_Off)
{
return atan2(_Off.e.y - _Off.s.y, _Off.e.x - _Off.s.x);
}
//半平面交排序函數[優先順序: 1.極角 2.前面的直線在後面的左邊]
static bool hpc_cmp(const pVector &l, const pVector &r)
{
double lp = hpc_pa(l), rp = hpc_pa(r);
if(fabs(lp - rp) > eps)
return lp < rp;
return point::xmult(l.s, r.e, r.s) < 0.0;
}
//用於計算的雙端隊列
pVector dequeue[maxpn];
//獲取半平面交的多邊形(多邊形的核)
//參數:向量集合[l],向量數量[ln];(半平面方向在向量左邊)
//函數運行後如果n[即返回多邊形的點數量]爲0則不存在半平面交的多邊形(不存在區域或區域面積無窮大)
polygon& halfPanelCross(pVector _Off[], int ln)
{
int i, tn;
n = 0;
std::sort(_Off, _Off + ln, hpc_cmp);
//平面在向量左邊的篩選
for(i = tn = 1; i < ln; i ++)
if(fabs(hpc_pa(_Off[i]) - hpc_pa(_Off[i - 1])) > eps)
_Off[tn ++] = _Off[i];
ln = tn;
int bot = 0, top = 1;
dequeue[0] = _Off[0];
dequeue[1] = _Off[1];
for(i = 2; i < ln; i ++)
{
if(dequeue[top].parallel(dequeue[top - 1]) ||
dequeue[bot].parallel(dequeue[bot + 1]))
return (*this);
while(bot < top &&
point::xmult(dequeue[top].crossLPt(dequeue[top - 1]), _Off[i].e, _Off[i].s) > eps)
top --;
while(bot < top &&
point::xmult(dequeue[bot].crossLPt(dequeue[bot + 1]), _Off[i].e, _Off[i].s) > eps)
bot ++;
dequeue[++ top] = _Off[i];
}
while(bot < top &&
point::xmult(dequeue[top].crossLPt(dequeue[top - 1]), dequeue[bot].e, dequeue[bot].s) > eps)
top --;
while(bot < top &&
point::xmult(dequeue[bot].crossLPt(dequeue[bot + 1]), dequeue[top].e, dequeue[top].s) > eps)
bot ++;
//計算交點(注意不同直線形成的交點可能重合)
if(top <= bot + 1)
return (*this);
for(i = bot; i < top; i ++)
pt[n ++] = dequeue[i].crossLPt(dequeue[i + 1]);
if(bot < top + 1)
pt[n ++] = dequeue[bot].crossLPt(dequeue[top]);
return (*this);
}
};
class circle
{
public:
point c;//圓心
double r;//半徑
double db, de;//圓弧度數起點, 圓弧度數終點(逆時針0-360)
//-------圓---------
//判斷圓在多邊形內
bool inside(const polygon &_Off) const
{
if(_Off.ahas(c) == false)
return false;
for(int i = 0; i < _Off.n; i ++)
{
pVector l = pVector(_Off.pt[i], _Off.pt[(i + 1) % _Off.n]);
if(l.dis(c, true) < r - eps)
return false;
}
return true;
}
//判斷多邊形在圓內(線段和折線類似)
bool has(const polygon &_Off) const
{
for(int i = 0; i < _Off.n; i ++)
if(_Off.pt[i].dis2(c) > r * r - eps)
return false;
return true;
}
//-------圓弧-------
//圓被其他圓截得的圓弧,參數:圓[_Off]
circle operator-(circle &_Off) const
{
//注意圓必須相交,圓心不能重合
double d2 = c.dis2(_Off.c);
double d = c.dis(_Off.c);
double ans = std::acos((d2 + r * r - _Off.r * _Off.r) / (2 * d * r));
point py = _Off.c - c;
double oans = std::atan2(py.y, py.x);
circle res;
res.c = c;
res.r = r;
res.db = oans + ans;
res.de = oans - ans + 2 * pi;
return res;
}
//圓被其他圓截得的圓弧,參數:圓[_Off]
circle operator+(circle &_Off) const
{
//注意圓必須相交,圓心不能重合
double d2 = c.dis2(_Off.c);
double d = c.dis(_Off.c);
double ans = std::acos((d2 + r * r - _Off.r * _Off.r) / (2 * d * r));
point py = _Off.c - c;
double oans = std::atan2(py.y, py.x);
circle res;
res.c = c;
res.r = r;
res.db = oans - ans;
res.de = oans + ans;
return res;
}
//過圓外一點的兩條切線
//參數:點[_Off](必須在圓外),返回:兩條切線(切線的s點爲_Off,e點爲切點)
std::pair<pVector, pVector> tangent(const point &_Off) const
{
double d = c.dis(_Off);
//計算角度偏移的方式
double angp = std::acos(r / d), ango = std::atan2(_Off.y - c.y, _Off.x - c.x);
point pl = point(c.x + r * std::cos(ango + angp), c.y + r * std::sin(ango + angp)),
pr = point(c.x + r * std::cos(ango - angp), c.y + r * std::sin(ango - angp));
return std::make_pair(pVector(_Off, pl), pVector(_Off, pr));
}
//計算直線和圓的兩個交點
//參數:直線[_Off](兩點式),返回兩個交點,注意直線必須和圓有兩個交點
std::pair<point, point> cross(pVector _Off) const
{
_Off.pton();
//到直線垂足的距離
double td = fabs(_Off.a * c.x + _Off.b * c.y + _Off.c) / sqrt(_Off.a * _Off.a + _Off.b * _Off.b);
//計算垂足座標
double xp = (_Off.b * _Off.b * c.x - _Off.a * _Off.b * c.y - _Off.a * _Off.c) / ( _Off.a * _Off.a + _Off.b * _Off.b);
double yp = (- _Off.a * _Off.b * c.x + _Off.a * _Off.a * c.y - _Off.b * _Off.c) / (_Off.a * _Off.a + _Off.b * _Off.b);
double ango = std::atan2(yp - c.y, xp - c.x);
double angp = std::acos(td / r);
return std::make_pair(point(c.x + r * std::cos(ango + angp), c.y + r * std::sin(ango + angp)),
point(c.x + r * std::cos(ango - angp), c.y + r * std::sin(ango - angp)));
}
};
class triangle
{
public:
point a, b, c;//頂點
triangle(){}
triangle(point a, point b, point c): a(a), b(b), c(c){}
//計算三角形面積
double area()
{
return fabs(point::xmult(a, b, c)) / 2.0;
}
//計算三角形外心
//返回:外接圓圓心
point circumcenter()
{
pVector u,v;
u.s.x = (a.x + b.x) / 2;
u.s.y = (a.y + b.y) / 2;
u.e.x = u.s.x - a.y + b.y;
u.e.y = u.s.y + a.x - b.x;
v.s.x = (a.x + c.x) / 2;
v.s.y = (a.y + c.y) / 2;
v.e.x = v.s.x - a.y + c.y;
v.e.y = v.s.y + a.x - c.x;
return u.crossLPt(v);
}
//計算三角形內心
//返回:內接圓圓心
point incenter()
{
pVector u, v;
double m, n;
u.s = a;
m = atan2(b.y - a.y, b.x - a.x);
n = atan2(c.y - a.y, c.x - a.x);
u.e.x = u.s.x + cos((m + n) / 2);
u.e.y = u.s.y + sin((m + n) / 2);
v.s = b;
m = atan2(a.y - b.y, a.x - b.x);
n = atan2(c.y - b.y, c.x - b.x);
v.e.x = v.s.x + cos((m + n) / 2);
v.e.y = v.s.y + sin((m + n) / 2);
return u.crossLPt(v);
}
//計算三角形垂心
//返回:高的交點
point perpencenter()
{
pVector u,v;
u.s = c;
u.e.x = u.s.x - a.y + b.y;
u.e.y = u.s.y + a.x - b.x;
v.s = b;
v.e.x = v.s.x - a.y + c.y;
v.e.y = v.s.y + a.x - c.x;
return u.crossLPt(v);
}
//計算三角形重心
//返回:重心
//到三角形三頂點距離的平方和最小的點
//三角形內到三邊距離之積最大的點
point barycenter()
{
pVector u,v;
u.s.x = (a.x + b.x) / 2;
u.s.y = (a.y + b.y) / 2;
u.e = c;
v.s.x = (a.x + c.x) / 2;
v.s.y = (a.y + c.y) / 2;
v.e = b;
return u.crossLPt(v);
}
//計算三角形費馬點
//返回:到三角形三頂點距離之和最小的點
point fermentpoint()
{
point u, v;
double step = fabs(a.x) + fabs(a.y) + fabs(b.x) + fabs(b.y) + fabs(c.x) + fabs(c.y);
int i, j, k;
u.x = (a.x + b.x + c.x) / 3;
u.y = (a.y + b.y + c.y) / 3;
while (step > eps)
{
for (k = 0; k < 10; step /= 2, k ++)
{
for (i = -1; i <= 1; i ++)
{
for (j =- 1; j <= 1; j ++)
{
v.x = u.x + step * i;
v.y = u.y + step * j;
if (u.dis(a) + u.dis(b) + u.dis(c) > v.dis(a) + v.dis(b) + v.dis(c))
u = v;
}
}
}
}
return u;
}
};
* 二維ACM計算幾何模板
* 注意變量類型更改和EPS
* #include <cmath>
* #include <cstdio>
* By OWenT
*/
const double eps = 1e-8;
const double pi = std::acos(-1.0);
//點
class point
{
public:
double x, y;
point(){};
point(double x, double y):x(x),y(y){};
static int xmult(const point &ps, const point &pe, const point &po)
{
return (ps.x - po.x) * (pe.y - po.y) - (pe.x - po.x) * (ps.y - po.y);
}
//相對原點的差乘結果,參數:點[_Off]
//即由原點和這兩個點組成的平行四邊形面積
double operator *(const point &_Off) const
{
return x * _Off.y - y * _Off.x;
}
//相對偏移
point operator - (const point &_Off) const
{
return point(x - _Off.x, y - _Off.y);
}
//點位置相同(double類型)
bool operator == (const point &_Off) const
{
return std::fabs(_Off.x - x) < eps && std::fabs(_Off.y - y) < eps;
}
//點位置不同(double類型)
bool operator != (const point &_Off) const
{
return ((*this) == _Off) == false;
}
//兩點間距離的平方
double dis2(const point &_Off) const
{
return (x - _Off.x) * (x - _Off.x) + (y - _Off.y) * (y - _Off.y);
}
//兩點間距離
double dis(const point &_Off) const
{
return std::sqrt((x - _Off.x) * (x - _Off.x) + (y - _Off.y) * (y - _Off.y));
}
};
//兩點表示的向量
class pVector
{
public:
point s, e;//兩點表示,起點[s],終點[e]
double a, b, c;//一般式,ax+by+c=0
pVector(){}
pVector(const point &s, const point &e):s(s),e(e){}
//向量與點的叉乘,參數:點[_Off]
//[點相對向量位置判斷]
double operator *(const point &_Off) const
{
return (_Off.y - s.y) * (e.x - s.x) - (_Off.x - s.x) * (e.y - s.y);
}
//向量與向量的叉乘,參數:向量[_Off]
double operator *(const pVector &_Off) const
{
return (e.x - s.x) * (_Off.e.y - _Off.s.y) - (e.y - s.y) * (_Off.e.x - _Off.s.x);
}
//從兩點表示轉換爲一般表示
bool pton()
{
a = s.y - e.y;
b = e.x - s.x;
c = s.x * e.y - s.y * e.x;
return true;
}
//-----------點和直線(向量)-----------
//點在向量左邊(右邊的小於號改成大於號即可,在對應直線上則加上=號)
//參數:點[_Off],向量[_Ori]
friend bool operator<(const point &_Off, const pVector &_Ori)
{
return (_Ori.e.y - _Ori.s.y) * (_Off.x - _Ori.s.x)
< (_Off.y - _Ori.s.y) * (_Ori.e.x - _Ori.s.x);
}
//點在直線上,參數:點[_Off]
bool lhas(const point &_Off) const
{
return std::fabs((*this) * _Off) < eps;
}
//點在線段上,參數:點[_Off]
bool shas(const point &_Off) const
{
return lhas(_Off)
&& _Off.x - std::min(s.x, e.x) > -eps && _Off.x - std::max(s.x, e.x) < eps
&& _Off.y - std::min(s.y, e.y) > -eps && _Off.y - std::max(s.y, e.y) < eps;
}
//點到直線/線段的距離
//參數: 點[_Off], 是否是線段[isSegment](默認爲直線)
double dis(const point &_Off, bool isSegment = false)
{
//化爲一般式
pton();
//到直線垂足的距離
double td = (a * _Off.x + b * _Off.y + c) / sqrt(a * a + b * b);
//如果是線段判斷垂足
if(isSegment)
{
double xp = (b * b * _Off.x - a * b * _Off.y - a * c) / ( a * a + b * b);
double yp = (-a * b * _Off.x + a * a * _Off.y - b * c) / (a * a + b * b);
double xb = std::max(s.x, e.x);
double yb = std::max(s.y, e.y);
double xs = s.x + e.x - xb;
double ys = s.y + e.y - yb;
if(xp > xb + eps || xp < xs - eps || yp > yb + eps || yp < ys - eps)
td = std::min(_Off.dis(s), _Off.dis(e));
}
return fabs(td);
}
//關於直線對稱的點
point mirror(const point &_Off) const
{
//注意先轉爲一般式
point ret;
double d = a * a + b * b;
ret.x = (b * b * _Off.x - a * a * _Off.x - 2 * a * b * _Off.y - 2 * a * c) / d;
ret.y = (a * a * _Off.y - b * b * _Off.y - 2 * a * b * _Off.x - 2 * b * c) / d;
return ret;
}
//計算兩點的中垂線
static pVector ppline(const point &_a, const point &_b)
{
pVector ret;
ret.s.x = (_a.x + _b.x) / 2;
ret.s.y = (_a.y + _b.y) / 2;
//一般式
ret.a = _b.x - _a.x;
ret.b = _b.y - _a.y;
ret.c = (_a.y - _b.y) * ret.s.y + (_a.x - _b.x) * ret.s.x;
//兩點式
if(std::fabs(ret.a) > eps)
{
ret.e.y = 0.0;
ret.e.x = - ret.c / ret.a;
if(ret.e == ret. s)
{
ret.e.y = 1e10;
ret.e.x = - (ret.c - ret.b * ret.e.y) / ret.a;
}
}
else
{
ret.e.x = 0.0;
ret.e.y = - ret.c / ret.b;
if(ret.e == ret. s)
{
ret.e.x = 1e10;
ret.e.y = - (ret.c - ret.a * ret.e.x) / ret.b;
}
}
return ret;
}
//------------直線和直線(向量)-------------
//直線重合,參數:直線向量[_Off]
bool equal(const pVector &_Off) const
{
return lhas(_Off.e) && lhas(_Off.s);
}
//直線平行,參數:直線向量[_Off]
bool parallel(const pVector &_Off) const
{
return std::fabs((*this) * _Off) < eps;
}
//兩直線交點,參數:目標直線[_Off]
point crossLPt(pVector _Off)
{
//注意先判斷平行和重合
point ret = s;
double t = ((s.x - _Off.s.x) * (_Off.s.y - _Off.e.y) - (s.y - _Off.s.y) * (_Off.s.x - _Off.e.x))
/ ((s.x - e.x) * (_Off.s.y - _Off.e.y) - (s.y - e.y) * (_Off.s.x - _Off.e.x));
ret.x += (e.x - s.x) * t;
ret.y += (e.y - s.y) * t;
return ret;
}
//------------線段和直線(向量)----------
//線段和直線交
//參數:線段[_Off]
bool crossSL(const pVector &_Off) const
{
double rs = (*this) * _Off.s;
double re = (*this) * _Off.e;
return rs * re < eps;
}
//------------線段和線段(向量)----------
//判斷線段是否相交(注意添加eps),參數:線段[_Off]
bool isCrossSS(const pVector &_Off) const
{
//1.快速排斥試驗判斷以兩條線段爲對角線的兩個矩形是否相交
//2.跨立試驗(等於0時端點重合)
return (
(std::max(s.x, e.x) >= std::min(_Off.s.x, _Off.e.x)) &&
(std::max(_Off.s.x, _Off.e.x) >= std::min(s.x, e.x)) &&
(std::max(s.y, e.y) >= std::min(_Off.s.y, _Off.e.y)) &&
(std::max(_Off.s.y, _Off.e.y) >= std::min(s.y, e.y)) &&
((pVector(_Off.s, s) * _Off) * (_Off * pVector(_Off.s, e)) >= 0.0) &&
((pVector(s, _Off.s) * (*this)) * ((*this) * pVector(s, _Off.e)) >= 0.0)
);
}
};
class polygon
{
public:
const static long maxpn = 100;
point pt[maxpn];//點(順時針或逆時針)
long n;//點的個數
point& operator[](int _p)
{
return pt[_p];
}
//求多邊形面積,多邊形內點必須順時針或逆時針
double area() const
{
double ans = 0.0;
int i;
for(i = 0; i < n; i ++)
{
int nt = (i + 1) % n;
ans += pt[i].x * pt[nt].y - pt[nt].x * pt[i].y;
}
return std::fabs(ans / 2.0);
}
//求多邊形重心,多邊形內點必須順時針或逆時針
point gravity() const
{
point ans;
ans.x = ans.y = 0.0;
int i;
double area = 0.0;
for(i = 0; i < n; i ++)
{
int nt = (i + 1) % n;
double tp = pt[i].x * pt[nt].y - pt[nt].x * pt[i].y;
area += tp;
ans.x += tp * (pt[i].x + pt[nt].x);
ans.y += tp * (pt[i].y + pt[nt].y);
}
ans.x /= 3 * area;
ans.y /= 3 * area;
return ans;
}
//判斷點在凸多邊形內,參數:點[_Off]
bool chas(const point &_Off) const
{
double tp = 0, np;
int i;
for(i = 0; i < n; i ++)
{
np = pVector(pt[i], pt[(i + 1) % n]) * _Off;
if(tp * np < -eps)
return false;
tp = (std::fabs(np) > eps)?np: tp;
}
return true;
}
//判斷點是否在任意多邊形內[射線法],O(n)
bool ahas(const point &_Off) const
{
int ret = 0;
double infv = 1e-10;//座標系最大範圍
pVector l = pVector(_Off, point( -infv ,_Off.y));
for(int i = 0; i < n; i ++)
{
pVector ln = pVector(pt[i], pt[(i + 1) % n]);
if(fabs(ln.s.y - ln.e.y) > eps)
{
point tp = (ln.s.y > ln.e.y)? ln.s: ln.e;
if(fabs(tp.y - _Off.y) < eps && tp.x < _Off.x + eps)
ret ++;
}
else if(ln.isCrossSS(l))
ret ++;
}
return (ret % 2 == 1);
}
//凸多邊形被直線分割,參數:直線[_Off]
polygon split(pVector _Off)
{
//注意確保多邊形能被分割
polygon ret;
point spt[2];
double tp = 0.0, np;
bool flag = true;
int i, pn = 0, spn = 0;
for(i = 0; i < n; i ++)
{
if(flag)
pt[pn ++] = pt[i];
else
ret.pt[ret.n ++] = pt[i];
np = _Off * pt[(i + 1) % n];
if(tp * np < -eps)
{
flag = !flag;
spt[spn ++] = _Off.crossLPt(pVector(pt[i], pt[(i + 1) % n]));
}
tp = (std::fabs(np) > eps)?np: tp;
}
ret.pt[ret.n ++] = spt[0];
ret.pt[ret.n ++] = spt[1];
n = pn;
return ret;
}
//-------------凸包-------------
//Graham掃描法,複雜度O(nlg(n)),結果爲逆時針
//#include <algorithm>
static bool graham_cmp(const point &l, const point &r)//凸包排序函數
{
return l.y < r.y || (l.y == r.y && l.x < r.x);
}
polygon& graham(point _p[], int _n)
{
int i, len;
std::sort(_p, _p + _n, polygon::graham_cmp);
n = 1;
pt[0] = _p[0], pt[1] = _p[1];
for(i = 2; i < _n; i ++)
{
while(n && point::xmult(_p[i], pt[n], pt[n - 1]) >= 0)
n --;
pt[++ n] = _p[i];
}
len = n;
pt[++ n] = _p[_n - 2];
for(i = _n - 3; i >= 0; i --)
{
while(n != len && point::xmult(_p[i], pt[n], pt[n - 1]) >= 0)
n --;
pt[++ n] = _p[i];
}
return (*this);
}
//凸包旋轉卡殼(注意點必須順時針或逆時針排列)
//返回值凸包直徑的平方(最遠兩點距離的平方)
double rotating_calipers()
{
int i = 1;
double ret = 0.0;
pt[n] = pt[0];
for(int j = 0; j < n; j ++)
{
while(fabs(point::xmult(pt[j], pt[j + 1], pt[i + 1])) > fabs(point::xmult(pt[j], pt[j + 1], pt[i])) + eps)
i = (i + 1) % n;
//pt[i]和pt[j],pt[i + 1]和pt[j + 1]可能是對踵點
ret = std::max(ret, std::max(pt[i].dis(pt[j]), pt[i + 1].dis(pt[j + 1])));
}
return ret;
}
//凸包旋轉卡殼(注意點必須逆時針排列)
//返回值兩凸包的最短距離
double rotating_calipers(polygon &_Off)
{
int i = 0;
double ret = 1e10;//inf
pt[n] = pt[0];
_Off.pt[_Off.n] = _Off.pt[0];
//注意凸包必須逆時針排列且pt[0]是左下角點的位置
while(_Off.pt[i + 1].y > _Off.pt[i].y)
i = (i + 1) % _Off.n;
for(int j = 0; j < n; j ++)
{
double tp;
//逆時針時爲 >,順時針則相反
while((tp = point::xmult(pt[j], pt[j + 1], _Off.pt[i + 1]) - point::xmult( pt[j], pt[j + 1], _Off.pt[i])) > eps)
i = (i + 1) % _Off.n;
//(pt[i],pt[i+1])和(_Off.pt[j],_Off.pt[j + 1])可能是最近線段
ret = std::min(ret, pVector(pt[j], pt[j + 1]).dis(_Off.pt[i], true));
ret = std::min(ret, pVector(_Off.pt[i], _Off.pt[i + 1]).dis(pt[j + 1], true));
if(tp > -eps)//如果不考慮TLE問題最好不要加這個判斷
{
ret = std::min(ret, pVector(pt[j], pt[j + 1]).dis(_Off.pt[i + 1], true));
ret = std::min(ret, pVector(_Off.pt[i], _Off.pt[i + 1]).dis(pt[j], true));
}
}
return ret;
}
//-----------半平面交-------------
//複雜度:O(nlog2(n))
//#include <algorithm>
//半平面計算極角函數[如果考慮效率可以用成員變量記錄]
static double hpc_pa(const pVector &_Off)
{
return atan2(_Off.e.y - _Off.s.y, _Off.e.x - _Off.s.x);
}
//半平面交排序函數[優先順序: 1.極角 2.前面的直線在後面的左邊]
static bool hpc_cmp(const pVector &l, const pVector &r)
{
double lp = hpc_pa(l), rp = hpc_pa(r);
if(fabs(lp - rp) > eps)
return lp < rp;
return point::xmult(l.s, r.e, r.s) < 0.0;
}
//用於計算的雙端隊列
pVector dequeue[maxpn];
//獲取半平面交的多邊形(多邊形的核)
//參數:向量集合[l],向量數量[ln];(半平面方向在向量左邊)
//函數運行後如果n[即返回多邊形的點數量]爲0則不存在半平面交的多邊形(不存在區域或區域面積無窮大)
polygon& halfPanelCross(pVector _Off[], int ln)
{
int i, tn;
n = 0;
std::sort(_Off, _Off + ln, hpc_cmp);
//平面在向量左邊的篩選
for(i = tn = 1; i < ln; i ++)
if(fabs(hpc_pa(_Off[i]) - hpc_pa(_Off[i - 1])) > eps)
_Off[tn ++] = _Off[i];
ln = tn;
int bot = 0, top = 1;
dequeue[0] = _Off[0];
dequeue[1] = _Off[1];
for(i = 2; i < ln; i ++)
{
if(dequeue[top].parallel(dequeue[top - 1]) ||
dequeue[bot].parallel(dequeue[bot + 1]))
return (*this);
while(bot < top &&
point::xmult(dequeue[top].crossLPt(dequeue[top - 1]), _Off[i].e, _Off[i].s) > eps)
top --;
while(bot < top &&
point::xmult(dequeue[bot].crossLPt(dequeue[bot + 1]), _Off[i].e, _Off[i].s) > eps)
bot ++;
dequeue[++ top] = _Off[i];
}
while(bot < top &&
point::xmult(dequeue[top].crossLPt(dequeue[top - 1]), dequeue[bot].e, dequeue[bot].s) > eps)
top --;
while(bot < top &&
point::xmult(dequeue[bot].crossLPt(dequeue[bot + 1]), dequeue[top].e, dequeue[top].s) > eps)
bot ++;
//計算交點(注意不同直線形成的交點可能重合)
if(top <= bot + 1)
return (*this);
for(i = bot; i < top; i ++)
pt[n ++] = dequeue[i].crossLPt(dequeue[i + 1]);
if(bot < top + 1)
pt[n ++] = dequeue[bot].crossLPt(dequeue[top]);
return (*this);
}
};
class circle
{
public:
point c;//圓心
double r;//半徑
double db, de;//圓弧度數起點, 圓弧度數終點(逆時針0-360)
//-------圓---------
//判斷圓在多邊形內
bool inside(const polygon &_Off) const
{
if(_Off.ahas(c) == false)
return false;
for(int i = 0; i < _Off.n; i ++)
{
pVector l = pVector(_Off.pt[i], _Off.pt[(i + 1) % _Off.n]);
if(l.dis(c, true) < r - eps)
return false;
}
return true;
}
//判斷多邊形在圓內(線段和折線類似)
bool has(const polygon &_Off) const
{
for(int i = 0; i < _Off.n; i ++)
if(_Off.pt[i].dis2(c) > r * r - eps)
return false;
return true;
}
//-------圓弧-------
//圓被其他圓截得的圓弧,參數:圓[_Off]
circle operator-(circle &_Off) const
{
//注意圓必須相交,圓心不能重合
double d2 = c.dis2(_Off.c);
double d = c.dis(_Off.c);
double ans = std::acos((d2 + r * r - _Off.r * _Off.r) / (2 * d * r));
point py = _Off.c - c;
double oans = std::atan2(py.y, py.x);
circle res;
res.c = c;
res.r = r;
res.db = oans + ans;
res.de = oans - ans + 2 * pi;
return res;
}
//圓被其他圓截得的圓弧,參數:圓[_Off]
circle operator+(circle &_Off) const
{
//注意圓必須相交,圓心不能重合
double d2 = c.dis2(_Off.c);
double d = c.dis(_Off.c);
double ans = std::acos((d2 + r * r - _Off.r * _Off.r) / (2 * d * r));
point py = _Off.c - c;
double oans = std::atan2(py.y, py.x);
circle res;
res.c = c;
res.r = r;
res.db = oans - ans;
res.de = oans + ans;
return res;
}
//過圓外一點的兩條切線
//參數:點[_Off](必須在圓外),返回:兩條切線(切線的s點爲_Off,e點爲切點)
std::pair<pVector, pVector> tangent(const point &_Off) const
{
double d = c.dis(_Off);
//計算角度偏移的方式
double angp = std::acos(r / d), ango = std::atan2(_Off.y - c.y, _Off.x - c.x);
point pl = point(c.x + r * std::cos(ango + angp), c.y + r * std::sin(ango + angp)),
pr = point(c.x + r * std::cos(ango - angp), c.y + r * std::sin(ango - angp));
return std::make_pair(pVector(_Off, pl), pVector(_Off, pr));
}
//計算直線和圓的兩個交點
//參數:直線[_Off](兩點式),返回兩個交點,注意直線必須和圓有兩個交點
std::pair<point, point> cross(pVector _Off) const
{
_Off.pton();
//到直線垂足的距離
double td = fabs(_Off.a * c.x + _Off.b * c.y + _Off.c) / sqrt(_Off.a * _Off.a + _Off.b * _Off.b);
//計算垂足座標
double xp = (_Off.b * _Off.b * c.x - _Off.a * _Off.b * c.y - _Off.a * _Off.c) / ( _Off.a * _Off.a + _Off.b * _Off.b);
double yp = (- _Off.a * _Off.b * c.x + _Off.a * _Off.a * c.y - _Off.b * _Off.c) / (_Off.a * _Off.a + _Off.b * _Off.b);
double ango = std::atan2(yp - c.y, xp - c.x);
double angp = std::acos(td / r);
return std::make_pair(point(c.x + r * std::cos(ango + angp), c.y + r * std::sin(ango + angp)),
point(c.x + r * std::cos(ango - angp), c.y + r * std::sin(ango - angp)));
}
};
class triangle
{
public:
point a, b, c;//頂點
triangle(){}
triangle(point a, point b, point c): a(a), b(b), c(c){}
//計算三角形面積
double area()
{
return fabs(point::xmult(a, b, c)) / 2.0;
}
//計算三角形外心
//返回:外接圓圓心
point circumcenter()
{
pVector u,v;
u.s.x = (a.x + b.x) / 2;
u.s.y = (a.y + b.y) / 2;
u.e.x = u.s.x - a.y + b.y;
u.e.y = u.s.y + a.x - b.x;
v.s.x = (a.x + c.x) / 2;
v.s.y = (a.y + c.y) / 2;
v.e.x = v.s.x - a.y + c.y;
v.e.y = v.s.y + a.x - c.x;
return u.crossLPt(v);
}
//計算三角形內心
//返回:內接圓圓心
point incenter()
{
pVector u, v;
double m, n;
u.s = a;
m = atan2(b.y - a.y, b.x - a.x);
n = atan2(c.y - a.y, c.x - a.x);
u.e.x = u.s.x + cos((m + n) / 2);
u.e.y = u.s.y + sin((m + n) / 2);
v.s = b;
m = atan2(a.y - b.y, a.x - b.x);
n = atan2(c.y - b.y, c.x - b.x);
v.e.x = v.s.x + cos((m + n) / 2);
v.e.y = v.s.y + sin((m + n) / 2);
return u.crossLPt(v);
}
//計算三角形垂心
//返回:高的交點
point perpencenter()
{
pVector u,v;
u.s = c;
u.e.x = u.s.x - a.y + b.y;
u.e.y = u.s.y + a.x - b.x;
v.s = b;
v.e.x = v.s.x - a.y + c.y;
v.e.y = v.s.y + a.x - c.x;
return u.crossLPt(v);
}
//計算三角形重心
//返回:重心
//到三角形三頂點距離的平方和最小的點
//三角形內到三邊距離之積最大的點
point barycenter()
{
pVector u,v;
u.s.x = (a.x + b.x) / 2;
u.s.y = (a.y + b.y) / 2;
u.e = c;
v.s.x = (a.x + c.x) / 2;
v.s.y = (a.y + c.y) / 2;
v.e = b;
return u.crossLPt(v);
}
//計算三角形費馬點
//返回:到三角形三頂點距離之和最小的點
point fermentpoint()
{
point u, v;
double step = fabs(a.x) + fabs(a.y) + fabs(b.x) + fabs(b.y) + fabs(c.x) + fabs(c.y);
int i, j, k;
u.x = (a.x + b.x + c.x) / 3;
u.y = (a.y + b.y + c.y) / 3;
while (step > eps)
{
for (k = 0; k < 10; step /= 2, k ++)
{
for (i = -1; i <= 1; i ++)
{
for (j =- 1; j <= 1; j ++)
{
v.x = u.x + step * i;
v.y = u.y + step * j;
if (u.dis(a) + u.dis(b) + u.dis(c) > v.dis(a) + v.dis(b) + v.dis(c))
u = v;
}
}
}
}
return u;
}
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.