Preface
做了几道题之后才知道为什么计算几何真的是毒瘤(手动捂脸)
为了方便接下来的向量不带箭头。
点积
表示:
几何定义: 在 上投影长度 * 的模长
代数定义:
连接两个向量,组成三角形,然后用余弦定理去证明。
叉积
表示:
几何定义: 与 的围成的平行四边形的有向面积。(如果 )则 在 左手向。
代数定义:
这个随便证明。
精度误差
是个小量,double下多取
半平面交
注意一下几个步骤
-
极角排序:
这个主要用 这个函数。
貌似这样常数很大,samjia说用叉积。
但叉积麻烦,其实可以加入直线的时候先算出来,排序的时候再直接调用。
-
确定直线方向
这点最重要了。
一般来说,如果是围绕原点或者某个定点,我们需要规定一个方向,可以在加入直线的时候用函数判一判。
但还是那句话,具体情况具体分析,有时候并不总围绕某个顶点时,而是只确定一个方向,这时候就要好好注意。
-
排序条件
排序条件中有一栏是当两个向量极角相同时,我们必须只能保留到原点较短的那个。
所以用atan2可以显得方便很多。
-
最后要用队尾更新队头
这个也很重要。
代码
struct Dot {
db x, y;
Dot (db _x = 0, db _y = 0) { x = _x, y = _y; }
friend Dot operator + (Dot a, Dot b) { return Dot(a.x + b.x, a.y + b.y); }
friend Dot operator - (Dot a, Dot b) { return Dot(a.x - b.x, a.y - b.y); }
friend db operator ^ (Dot a, Dot b) { return (a.x * b.y - b.x * a.y); }
friend db operator * (Dot a, Dot b) { return (a.x * b.x + a.y * b.y); }
friend Dot operator * (Dot a, db b) { return Dot(a.x * b, a.y * b); }
}
struct Line {
Dot p, v; db px; // px是极角
Line () {}
Line (Dot _p, Dot _v) { p = _p, v = _v; }
friend bool operator < (Line a, Line b) { return a.px - b.px < -e; }
}
db len(Dot a) { return sqrt(a ^ a); } // 模长
bool in(Line a, Dot b) { return a.v * (b - a.p) <= 0; } // 判断点b在直线a的左手向则返回1
db distl(Line a, Dot b) { return fabs(a.v * (b - a.p) / len(a.v))} // 点b到直线a的距离,运用叉积除以底得到高
Dot Ict(Line a, Line b) { return Dot(b.p + b.v * (a.v * (a.p - b.p) / (a.v * b.v))); } // 比例法,记住那副图,还有中间的a.p - b.p
上面几个操作就可以做半平面交了:
sort(d + 1, d + L + 1);
int h = 1, t = 1;
que[1] = d[1];
F(i, 2, L) {
if (fabs(d[i].px - d[i - 1].px) <= e) continue;
while (h < t && in(d[i], Ict(que[t - 1], que[t]))) t --;
while (h < t && in(d[i], Ict(que[h], que[h + 1]))) h ++;
que[++ t] = d[i];
}
while (h < t && in(que[h], Ict(que[t], que[t - 1]))) t --;
以上.