PDF文档中表格以及形状解析-后续处理(长线段根据交点打断成短线段)

发布于:2025-03-10 ⋅ 阅读:(17) ⋅ 点赞:(0)

问题描述
给定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条小线段。结果与预期一致。