点到折线的最近距离计算

文章讲述了如何计算点到折线的最近距离,包括对折线进行密集采样和求解线段投影的方法。在处理点在折线内外的不同情况时,需要判断投影点的位置并可能进行额外的处理。在实际应用中,特别是计算平行折线之间的间隔,可以利用线段的平行性优化计算。提供了一段C++代码实现该算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

定义一下,什么是“点到折线的最近距离”:
如果折线是由无线细分的点构成的,能找到一个点,与待求点的距离最近,这个最近距离是所需要的。
但是实际上,一条折线总是由有限的点构成的,计算与构成折线的点的最近距离,并不意味着就是点到折线的最近距离。
这时候,一种方式是对折线进行根据精度要求进行密集采样,计算出最近的点的距离;
另外,就是求取到构成折线的线段的最近的距离。
为了效率考虑,往往用第二种。
第二种方法也有它的问题:点到所有的线段都会有投影距离,投影距离最小的未必是对的,因为投影点可能在线段外部。甚至这个点到所有线段的投影点,都会在线段的外部,而且是很常见的。
所以,又需要去做进一步的处理:如果能找到投影点在线段内部的,那么点与这个投影点的距离就是所求的数值了;如果没有找到,且待求点确实是在折线内部,那么就要去判断投影点,与哪个线段的端点距离最小,就这个作为返回,如果要精细,就可以在这个线段与相邻线段,进行密集采样得到一系列点,找出最近的点的距离;如果待求点在外部,那么就找最近的线段的投影距离(实际上可以归纳到第二种情况)。

在应用中,是为了求取两条基本平行的折线的间隔距离,此时,可以结合线段的平行情况来去计算,而不是盲目的计算点到折线的距离。

一段测试代码:

// 返回点到折线上的投影点
            std::tuple<Point3D, double, size_t> Polyline3D::GetProjectedPoint(
                const Point3D& point) const {
                bool is_within = this->IsWithin(point);
                if (is_within) {
                    bool inside_line_seg = false;
                    auto pt              = point.ToBoostPoint3D();

                    Point3D projectedPoint;
                    double  minDistance       = std::numeric_limits<double>::max();
                    size_t  choose_ln_seg_idx = 0;

                    Point3D all_projectedPoint;
                    double  all_minPrjDistance        = std::numeric_limits<double>::max();
                    double  to_end_point_min_distance = std::numeric_limits<double>::max();
                    size_t  choose_ln_seg_idx_outside = 0;

                    for (size_t i = 0; i < polyline_.size() - 1; ++i) {
                        const auto& startPoint = polyline_[i];
                        const auto& endPoint   = polyline_[i + 1];

                        LineSegment3D ln(Point3D(bg::get<0>(startPoint), bg::get<1>(startPoint), bg::get<2>(startPoint)),
                                         Point3D(bg::get<0>(endPoint), bg::get<1>(endPoint), bg::get<2>(endPoint)));

                        auto   pjp      = ln.GetProjectedPoint(point);
                        double distance = point.Distance(pjp);

                        if (ln.IsInside(point)) {
                            inside_line_seg = true;

                            // 更新最小距离和投影点
                            if (distance < minDistance) {
                                choose_ln_seg_idx = i;
                                minDistance       = distance;
                                projectedPoint    = pjp;
                            }
                        } else {
                            double each_min_dist = ln.GetMinDistance(point);
                            if (each_min_dist < to_end_point_min_distance) {
                                all_minPrjDistance        = distance;
                                all_projectedPoint        = pjp;
                                to_end_point_min_distance = each_min_dist;
                                choose_ln_seg_idx_outside = i;
                            }
                        }
                    }

                    if (inside_line_seg) {
                        return std::make_tuple(projectedPoint, minDistance, choose_ln_seg_idx);
                    } else {
                        return std::make_tuple(all_projectedPoint, all_minPrjDistance, choose_ln_seg_idx_outside);
                    }

                    //

                } else {
                    auto sp = (polyline_[0]);
                    auto ep = polyline_.back();

                    Point3D spt(bg::get<0>(sp), bg::get<1>(sp), bg::get<2>(sp));
                    Point3D ept(bg::get<0>(ep), bg::get<1>(ep), bg::get<2>(ep));

                    LineSegment3D ln;
                    size_t        ass_ln_seg_idx = 0;
                    if (spt.Distance(point) < ept.Distance(point)) {
                        GetFirstLineSegment(ln);
                    } else {
                        GetLastLineSegment(ln);
                        ass_ln_seg_idx = polyline_.size() - 1;
                    }
                    auto prj  = ln.GetProjectedPoint(point);
                    auto dist = point.Distance(prj);
                    return std::make_tuple(prj, dist, ass_ln_seg_idx);
                }
            }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值