问题描述
给定n条线段,要求根据它们的交点将线段打断,生成新的线段集合。
定义结构:
代码如下:
typedef struct tagStruVertex
{
double x;
double y;
tagStruVertex(double x = 0, double y = 0) : x(x), y(y) {}
// 重载运算符,用于排序和去重
bool operator<(const tagStruVertex& other) const {
if (x != other.x) return x < other.x;
return y < other.y;
}
bool operator==(const tagStruVertex& other) const
{
return fabs(x - other.x) < 1e-9 && fabs(y - other.y) < 1e-9;
}
bool operator!=(const tagStruVertex& other) const {
if (x != other.x || y != other.y)
return true;
return false;
}
double distanceTo(
const tagStruVertex& point) const
{
double disX = x - point.x;
double disY = y - point.y;
return sqrt(disX * disX + disY * disY);
}
}Vertex;
typedef struct tagStruSegment
{
Vertex start, end;
tagStruSegment(Vertex p1, Vertex p2) : start(p1), end(p2) {}
tagStruSegment() {}
// 重载运算符,用于 set 去重
bool operator<(const tagStruSegment& other) const {
if (start == other.start) return end < other.end;
return start < other.start;
}
bool operator==(const tagStruSegment& other) const {
if ((start == other.start && end == other.end) || (start == other.end && end == other.start))
return true;
else
return false;
}
}Segment;
判断交点
使用线段相交的几何算法来检测两条线段是否相交。计算交点坐标,如果相交,则返回交点。
代码如下:
// 计算叉积
double cross(const Vertex& o, const Vertex& a, const Vertex& b) {
return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
}
//判断点是否在线段上
bool IsPointOnSegment(const Vertex& p, const Segment& seg) {
double crossProd = cross(seg.start, seg.end, p);
if (fabs(crossProd) > 1e-9) return false; // 不在直线上
double minX = min(seg.start.x, seg.end.x);
double maxX = max(seg.start.x, seg.end.x);
double minY = min(seg.start.y, seg.end.y);
double maxY = max(seg.start.y, seg.end.y);
return (p.x >= minX - 1e-9 && p.x <= maxX + 1e-9) &&
(p.y >= minY - 1e-9 && p.y <= maxY + 1e-9);
}
//判断两条线段是否相交:
bool IsCross(const Segment& seg1, const Segment& seg2, Vertex& intersect) {
Vertex a = seg1.start, b = seg1.end;
Vertex c = seg2.start, d = seg2.end;
double cross1 = cross(a, b, c);
double cross2 = cross(a, b, d);
double cross3 = cross(c, d, a);
double cross4 = cross(c, d, b);
// 判断是否相交
if (((cross1 * cross2) < 0) && ((cross3 * cross4) < 0)) {
// 计算交点
double t = cross3 / (cross3 - cross4);
intersect.x = a.x + t * (b.x - a.x);
intersect.y = a.y + t * (b.y - a.y);
return true;
}
// 处理共线情况
if (fabs(cross1) < 1e-9 && IsPointOnSegment(c, seg1)) { intersect = c; return true; }
if (fabs(cross2) < 1e-9 && IsPointOnSegment(d, seg1)) { intersect = d; return true; }
if (fabs(cross3) < 1e-9 && IsPointOnSegment(a, seg2)) { intersect = a; return true; }
if (fabs(cross4) < 1e-9 && IsPointOnSegment(b, seg2)) { intersect = b; return true; }
return false;
}
线段根据交点打断后生成新的线段
代码如下:
// 打断线段
vector<Segment> AdjustSegment(const Segment& seg, const set<Vertex>& intersections) {
vector<Segment> vecSegments;
vector<Vertex> points;
// 将起点和终点加入
points.push_back(seg.start);
for (const auto& p : intersections) {
if (IsPointOnSegment(p, seg)) {
points.push_back(p);
}
}
points.push_back(seg.end);
// 按x坐标排序(或按y坐标,取决于线段方向)
sort(points.begin(), points.end());
// 生成小线段
for (size_t i = 1; i < points.size(); ++i) {
vecSegments.push_back(Segment(points[i - 1], points[i]));
}
return vecSegments;
}
vector<Segment> AdjustSegments(const vector<Segment>& segments) {
vector<Segment> vecSegments;
for (size_t i = 0; i < segments.size(); ++i) {
set<Vertex> intersections;
// 查找当前线段与其他线段的交点
for (size_t j = 0; j < segments.size(); ++j) {
if (i == j) continue;
Vertex intersect;
if (IsCross(segments[i], segments[j], intersect)) {
intersections.insert(intersect);
}
}
// 打断当前线段
vector<Segment> lines = AdjustSegment(segments[i], intersections);
vecSegments.insert(vecSegments.end(), lines.begin(), lines.end());
}
// 使用 set 去重
set<Segment> uniqueSegments(vecSegments.begin(), vecSegments.end());
return vector<Segment>(uniqueSegments.begin(), uniqueSegments.end());
}
调用测试
代码如下:
vector<Segment> segments = {
Segment(Vertex(0, 0), Vertex(30, 0)),
Segment(Vertex(0, 10), Vertex(30, 10)),
Segment(Vertex(0, 20), Vertex(30, 20)),
Segment(Vertex(0, 30), Vertex(30, 30)),
Segment(Vertex(0, 0), Vertex(0, 30)),
Segment(Vertex(10, 0), Vertex(10, 30)),
Segment(Vertex(20, 0), Vertex(20, 30)),
Segment(Vertex(30, 0), Vertex(30, 30))
};
vector<Segment> vecSegments = AdjustSegments(segments);
//过滤掉长度为0的线段
for (auto it = vecSegments.begin(); it != vecSegments.end(); ) {
if (it->start.distanceTo(it->end) == 0)
it = vecSegments.erase(it); // 如果匹配,删除元素并更新迭代器
else
++it; // 如果不匹配,继续遍历
}
//处理完成最终vecSegments里边的结果即为打断后的所有小线段
处理完得到小线段的数量为24条小线段。结果与预期一致。