计算几何(伪,非正解)
90dcb0c3-23f9-468b-83da-fb64eeb22a45
前言
最近在学计算几何,但是发现只有向量版的讲解,蒟蒻表示这个对初中生真的很不友好,需要用到高中几何知识费脑子,那么,有没有一种不需要用到向量就能完成平面几何基本运算的计算方法呢?
正文
精度处理
精度,是计算几何中的一个重要问题。我们使用double类型存储,因为在运算过程中存在各种各样的因精度而引起的数值的改变,因此,我们不能直接比较两个浮点数是否相等,而是应该确定一个精度限制,即在某范围内的误差是允许的
#define espar 0.00000000000001
点的存储
点的储存可以使用两个double类型的变量x和y,分别表示点的横纵坐标。
我们定义一个结构体Point,用来维护一些和点有关的基本运算:判等,计算距离,是否重合……
#define inf 2147483647.2147483647
struct Point {//点
double x, y;//存储点的横纵坐标
inline bool same(double x) { return x < espar; }//判断是否相等(允许误差)
inline double nega(double x) { return x == 0 ? 0 : -x; }//取反
inline double Abs(double x) { if (x < 0) return nega(x); return x; }//取双精度浮点数的绝对值
inline bool Same(double a, double b) { if (same(Abs(a - b))) return true; return false; }//判断两个双精度浮点数是否相同(允许误差)
inline bool superposition(Point a, Point b) { if (Same(a.x, b.x) && Same(a.y, b.y)) return true; return false; }//判断两点是否重合
};
线段的计算
我们把线分为两种:线段和直线(射线暂不做讨论),这里讲解线段的相关计算
线段,首先需要两个端点和一个解析式,我们使用ax+by+c=0的变形公式ax+by=c来表示一条线段。对于一条线段,我们定义两个Point类型的变量start和end,表示线段的两个端点
线段在计算前的基本操作(大部分和点的操作一样):判等,取反,计算距离……
Point start, end;//存储线段的两个端点
double a, b, c;//以ax+by=c的形式存储线段解析式
inline double nega(double x) { return x == 0 ? 0 : -x; }//取反
inline void Swap(Point& x, Point& y) { Point z = y; y = x; x = z; }//交换
inline double Abs(double x) { if (x < 0) return nega(x); }//取绝对值
inline bool Same(double a, double b) { if (Abs(a - b) <= espar) return true; return false; }//判断两个数是否相等
inline double Min(double a1, double b1, double c1, double d1) { double mint = -inf; if (a1 < mint) mint = a1; if (b1 < mint) mint = b1; if (c1 < mint) mint = c1; if (d1 < mint) mint = d1; return mint; }//计算最小值
inline double Max(double a1, double b1, double c1, double d1) { double maxt = inf; if (a1 > maxt) maxt = a1; if (b1 > maxt) maxt = b1; if (c1 > maxt) maxt = c1; if (d1 > maxt) maxt = d1; return maxt; }//计算最大值
inline double distance(Point a, Point b) { return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); }//计算两点间的距离
线段解析式的计算:我们通过联立端点的坐标,可以用端点坐标表示出线段的解析式,最后得到的公式是:a=y1-y2,b=x2-x1,c=y1x2-y2x1
inline void compute(Point x, Point y) {//计算线段解析式,Point a,Point b为线段两个端点
if (x.x < y.x) Swap(x, y);
start = x, end = y; a = x.y - y.y, b = x.x - y.x, c = x.y * y.x - x.x * y.y;//通过公式计算线段解析式
if (a < 0 && b < 0 || Same(a, 0.0) && b < 0 || Same(b, 0.0) && a < 0) a = nega(a), b = nega(b), c = nega(c);
}
之后就是两条线段之间的操作:判断位置关系,计算交点……
inline int Line_Position(Line a1, Line b1) {
if (Same(nega(a1.a / a1.b) * nega(b1.a / b1.b), nega(1.0))) return 1;//k1*k2=-1,两线段垂直
if (Same(nega(a1.a / a1.b), nega(b1.a / b1.b)) && !Same((a1.c / a1.b), (b1.c / b1.b))) return 2;//k相等,且b1不相等,说明两线段所在直线平行
if (Same(nega(a1.a / a1.b), nega(b1.a / b1.b)) && Same((a1.c / a1.b), (b1.c / b1.b)) && Same(a1.start.x, b1.start.x) && Same(a1.start.y, b1.start.y) && Same(a1.end.x, b1.end.x) && Same(a1.end.y, b1.end.y)) return 3;//k相等,且b1相等,且两线段的端点完全相同说明两线段所在直线重合
}
inline Point intersection(Line a1, Line b1) {//计算线段a和线段b1的交点
Point ans;
if (Line_Position(a1, b1) == 2 || Line_Position(a1, b1) == 3) { ans.x = inf, ans.y = inf; return ans; }//如果两线段所在直线平行或重合,说明他们没有单一交点
//根据公式计算交点
ans.x = (b1.c - (a1.c * b1.b / a1.b)) / (b1.a - (a1.a * b1.b / a1.b));
ans.y = (a1.c - a1.a * ans.x) / a1.b;
//判断交点在不在线段上,不在的话就赋为极值
if (ans.x < Min(a1.start.x, b1.start.x, a1.end.x, b1.end.x)) ans.x = -inf;
if (ans.x > Max(a1.end.x, b1.end.x, a1.start.x, b1.start.x)) ans.x = inf;
if (ans.y < Min(a1.start.y, b1.start.y, a1.end.y, b1.end.y)) ans.y = -inf;
if (ans.y > Max(a1.start.y, b1.start.y, a1.end.y, b1.end.y)) ans.y = inf;
return ans;
}
inline bool Hasintersection(Line a1, Line b1) {
Point point = intersection(a1, b1);//获取交点坐标
if (point.x != inf && point.x != -inf && point.y != inf && point.y != -inf) return true;//两线段有一个在线段上的交点
if (point.x == inf || point.x == -inf || point.y == inf || point.y == -inf) return false;//两线段无在线段上的交点
}
然后是线段和点的位置的判断:在线段上,在线段外
inline int Point_Position(Point x) {//判断一个点与线段的位置关系
//如果这个点在线段的端点范围外
if (x.x < Min(start.x, start.x, end.x, end.x)) return 2;
if (x.x > Max(end.x, end.x, start.x, start.x)) return 2;
if (x.y < Min(start.y, start.y, end.y, end.y)) return 2;
if (x.y > Max(start.y, start.y, end.y, end.y)) return 2;
if (!Same(a * x.x + b * x.y, c)) return 2;//如果这个点的x坐标和y坐标不满足线段解析式,说明不在线段上
if (Same(a * x.x + b * x.y, c)) return 1;//如果满足,说明在线段上
}
最后是对线段长度的计算:
inline double Distance() { return distance(start, end); }//计算线段长度
直线的计算
直线的计算和线段的计算类似,只是没有了端点的限制,这里就不做讲解(代码里有讲解,也可以参照线段的讲解),直接贴代码:
struct Straight_Line {//直线
double a, b, c;//以ax+by=c的形式存储线段解析式
inline double nega(double x) { return x == 0 ? 0 : -x; }//取反
inline void Swap(Point& x, Point& y) { Point z = y; y = x; x = z; }//交换
inline double Abs(double x) { if (x < 0) return nega(x); }//取绝对值
inline bool Same(double a, double b) { if (Abs(a - b) <= espar) return true; return false; }//判断两个数是否相等
inline double Min(double a1, double b1, double c1, double d1) { double mint = -inf; if (a1 < mint) mint = a1; if (b1 < mint) mint = b1; if (c1 < mint) mint = c1; if (d1 < mint) mint = d1; return mint; }//计算最小值
inline double Max(double a1, double b1, double c1, double d1) { double maxt = inf; if (a1 > maxt) maxt = a1; if (b1 > maxt) maxt = b1; if (c1 > maxt) maxt = c1; if (d1 > maxt) maxt = d1; return maxt; }//计算最大值
inline void compute(Point x, Point y) {//计算线段解析式,Point a,Point b为直线上的两个点
if (x.x < y.x) Swap(x, y);
a = x.y - y.y, b = x.x - y.x, c = x.y * y.x - x.x * y.y;//通过公式计算直线解析式
if (a < 0 && b < 0 || Same(a, 0.0) && b < 0 || Same(b, 0.0) && a < 0) a = nega(a), b = nega(b), c = nega(c);
}
inline int Line_Position(Straight_Line a1, Straight_Line b1) {
if (Same(nega(a1.a / a1.b) * nega(b1.a / b1.b), nega(1.0))) return 1;//k1*k2=-1,两直线垂直
if (Same(nega(a1.a / a1.b), nega(b1.a / b1.b)) && !Same((a1.c / a1.b), (b1.c / b1.b))) return 2;//k相等,且b1不相等,说明两直线平行
if (Same(nega(a1.a / a1.b), nega(b1.a / b1.b)) && Same((a1.c / a1.b), (b1.c / b1.b))) return 3;//k相等,且b1相等,说明两直线重合
}
inline Point intersection(Straight_Line a1, Straight_Line b1) {//计算线段a和线段b1的交点
Point ans;
if (Line_Position(a1, b1) == 2 || Line_Position(a1, b1) == 3) { ans.x = inf, ans.y = inf; return ans; }//如果两直线平行或重合,说明他们没有单一交点
//根据公式计算交点
ans.x = (b1.c - (a1.c * b1.b / a1.b)) / (b1.a - (a1.a * b1.b / a1.b));
ans.y = (a1.c - a1.a * ans.x) / a1.b;
return ans;
}
inline bool Hasintersection(Straight_Line a1, Straight_Line b1) {
Point point = intersection(a1, b1);//获取交点坐标
if (point.x != inf && point.x != -inf && point.y != inf && point.y != -inf) return true;//两线段有一个在线段上的交点
if (point.x == inf || point.x == -inf || point.y == inf || point.y == -inf) return false;//两线段无在线段上的交点
}
inline int Point_Position(Point x) {//判断一个点与直线的位置关系
if (!Same(a * x.x + b * x.y, c)) return 2;//如果这个点的x坐标和y坐标不满足直线解析式,说明不在直线上
if (Same(a * x.x + b * x.y, c)) return 1;//如果满足,说明在直线上
}
};
总结
目前的完整代码(后续会继续更新)
#include<cstdio>
#include<cmath>
using namespace std;
#define espar 0.00000000000001
#define inf 2147483647.2147483647
struct Point {//点
double x, y;//存储点的横纵坐标
inline bool same(double x) { return x < espar; }//判断是否相等(允许误差)
inline double nega(double x) { return x == 0 ? 0 : -x; }//取反
inline double Abs(double x) { if (x < 0) return nega(x); return x; }//取双精度浮点数的绝对值
inline bool Same(double a, double b) { if (same(Abs(a - b))) return true; return false; }//判断两个双精度浮点数是否相同(允许误差)
inline bool superposition(Point a, Point b) { if (Same(a.x, b.x) && Same(a.y, b.y)) return true; return false; }//判断两点是否重合
};
struct Line {//线段
Point start, end;//存储线段的两个端点
double a, b, c;//以ax+by=c的形式存储线段解析式
inline double nega(double x) { return x == 0 ? 0 : -x; }//取反
inline void Swap(Point& x, Point& y) { Point z = y; y = x; x = z; }//交换
inline double Abs(double x) { if (x < 0) return nega(x); }//取绝对值
inline bool Same(double a, double b) { if (Abs(a - b) <= espar) return true; return false; }//判断两个数是否相等
inline double Min(double a1, double b1, double c1, double d1) { double mint = -inf; if (a1 < mint) mint = a1; if (b1 < mint) mint = b1; if (c1 < mint) mint = c1; if (d1 < mint) mint = d1; return mint; }//计算最小值
inline double Max(double a1, double b1, double c1, double d1) { double maxt = inf; if (a1 > maxt) maxt = a1; if (b1 > maxt) maxt = b1; if (c1 > maxt) maxt = c1; if (d1 > maxt) maxt = d1; return maxt; }//计算最大值
inline double distance(Point a, Point b) { return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); }//计算两点间的距离
inline void compute(Point x, Point y) {//计算线段解析式,Point a,Point b为线段两个端点
if (x.x < y.x) Swap(x, y);
start = x, end = y; a = x.y - y.y, b = x.x - y.x, c = x.y * y.x - x.x * y.y;//通过公式计算线段解析式
if (a < 0 && b < 0 || Same(a, 0.0) && b < 0 || Same(b, 0.0) && a < 0) a = nega(a), b = nega(b), c = nega(c);
}
inline int Line_Position(Line a1, Line b1) {
if (Same(nega(a1.a / a1.b) * nega(b1.a / b1.b), nega(1.0))) return 1;//k1*k2=-1,两线段垂直
if (Same(nega(a1.a / a1.b), nega(b1.a / b1.b)) && !Same((a1.c / a1.b), (b1.c / b1.b))) return 2;//k相等,且b1不相等,说明两线段所在直线平行
if (Same(nega(a1.a / a1.b), nega(b1.a / b1.b)) && Same((a1.c / a1.b), (b1.c / b1.b)) && Same(a1.start.x, b1.start.x) && Same(a1.start.y, b1.start.y) && Same(a1.end.x, b1.end.x) && Same(a1.end.y, b1.end.y)) return 3;//k相等,且b1相等,且两线段的端点完全相同说明两线段所在直线重合
}
inline Point intersection(Line a1, Line b1) {//计算线段a和线段b1的交点
Point ans;
if (Line_Position(a1, b1) == 2 || Line_Position(a1, b1) == 3) { ans.x = inf, ans.y = inf; return ans; }//如果两线段所在直线平行或重合,说明他们没有单一交点
//根据公式计算交点
ans.x = (b1.c - (a1.c * b1.b / a1.b)) / (b1.a - (a1.a * b1.b / a1.b));
ans.y = (a1.c - a1.a * ans.x) / a1.b;
//判断交点在不在线段上,不在的话就赋为极值
if (ans.x < Min(a1.start.x, b1.start.x, a1.end.x, b1.end.x)) ans.x = -inf;
if (ans.x > Max(a1.end.x, b1.end.x, a1.start.x, b1.start.x)) ans.x = inf;
if (ans.y < Min(a1.start.y, b1.start.y, a1.end.y, b1.end.y)) ans.y = -inf;
if (ans.y > Max(a1.start.y, b1.start.y, a1.end.y, b1.end.y)) ans.y = inf;
return ans;
}
inline bool Hasintersection(Line a1, Line b1) {
Point point = intersection(a1, b1);//获取交点坐标
if (point.x != inf && point.x != -inf && point.y != inf && point.y != -inf) return true;//两线段有一个在线段上的交点
if (point.x == inf || point.x == -inf || point.y == inf || point.y == -inf) return false;//两线段无在线段上的交点
}
inline double Distance() { return distance(start, end); }//计算线段长度
inline int Point_Position(Point x) {//判断一个点与线段的位置关系
//如果这个点在线段的端点范围外
if (x.x < Min(start.x, start.x, end.x, end.x)) return 2;
if (x.x > Max(end.x, end.x, start.x, start.x)) return 2;
if (x.y < Min(start.y, start.y, end.y, end.y)) return 2;
if (x.y > Max(start.y, start.y, end.y, end.y)) return 2;
if (!Same(a * x.x + b * x.y, c)) return 2;//如果这个点的x坐标和y坐标不满足线段解析式,说明不在线段上
if (Same(a * x.x + b * x.y, c)) return 1;//如果满足,说明在线段上
}
};
struct Straight_Line {//直线
double a, b, c;//以ax+by=c的形式存储线段解析式
inline double nega(double x) { return x == 0 ? 0 : -x; }//取反
inline void Swap(Point& x, Point& y) { Point z = y; y = x; x = z; }//交换
inline double Abs(double x) { if (x < 0) return nega(x); }//取绝对值
inline bool Same(double a, double b) { if (Abs(a - b) <= espar) return true; return false; }//判断两个数是否相等
inline double Min(double a1, double b1, double c1, double d1) { double mint = -inf; if (a1 < mint) mint = a1; if (b1 < mint) mint = b1; if (c1 < mint) mint = c1; if (d1 < mint) mint = d1; return mint; }//计算最小值
inline double Max(double a1, double b1, double c1, double d1) { double maxt = inf; if (a1 > maxt) maxt = a1; if (b1 > maxt) maxt = b1; if (c1 > maxt) maxt = c1; if (d1 > maxt) maxt = d1; return maxt; }//计算最大值
inline void compute(Point x, Point y) {//计算线段解析式,Point a,Point b为直线上的两个点
if (x.x < y.x) Swap(x, y);
a = x.y - y.y, b = x.x - y.x, c = x.y * y.x - x.x * y.y;//通过公式计算直线解析式
if (a < 0 && b < 0 || Same(a, 0.0) && b < 0 || Same(b, 0.0) && a < 0) a = nega(a), b = nega(b), c = nega(c);
}
inline int Line_Position(Straight_Line a1, Straight_Line b1) {
if (Same(nega(a1.a / a1.b) * nega(b1.a / b1.b), nega(1.0))) return 1;//k1*k2=-1,两直线垂直
if (Same(nega(a1.a / a1.b), nega(b1.a / b1.b)) && !Same((a1.c / a1.b), (b1.c / b1.b))) return 2;//k相等,且b1不相等,说明两直线平行
if (Same(nega(a1.a / a1.b), nega(b1.a / b1.b)) && Same((a1.c / a1.b), (b1.c / b1.b))) return 3;//k相等,且b1相等,说明两直线重合
}
inline Point intersection(Straight_Line a1, Straight_Line b1) {//计算线段a和线段b1的交点
Point ans;
if (Line_Position(a1, b1) == 2 || Line_Position(a1, b1) == 3) { ans.x = inf, ans.y = inf; return ans; }//如果两直线平行或重合,说明他们没有单一交点
//根据公式计算交点
ans.x = (b1.c - (a1.c * b1.b / a1.b)) / (b1.a - (a1.a * b1.b / a1.b));
ans.y = (a1.c - a1.a * ans.x) / a1.b;
return ans;
}
inline bool Hasintersection(Straight_Line a1, Straight_Line b1) {
Point point = intersection(a1, b1);//获取交点坐标
if (point.x != inf && point.x != -inf && point.y != inf && point.y != -inf) return true;//两线段有一个在线段上的交点
if (point.x == inf || point.x == -inf || point.y == inf || point.y == -inf) return false;//两线段无在线段上的交点
}
inline int Point_Position(Point x) {//判断一个点与直线的位置关系
if (!Same(a * x.x + b * x.y, c)) return 2;//如果这个点的x坐标和y坐标不满足直线解析式,说明不在直线上
if (Same(a * x.x + b * x.y, c)) return 1;//如果满足,说明在直线上
}
};
这篇文章到这里就结束了,但是计算几何的内容远不止于此,后续代码和讲解会继续更新。喜欢的话点个赞吧ヾ( ̄▽ ̄)Bye~Bye~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下