简化点集合 道格拉斯-普克算法(Douglas-Peucker Algorithm)

c#

csharp 复制代码
```csharp
    /// <summary>
    /// 使用 Douglas-Peucker 算法简化点集合
    /// </summary>
    /// <param name="points">原始点集</param>
    /// <param name="tolerance">简化阈值(距离),建议 0.01 ~ 0.5 mm</param>
    /// <returns>简化后的点集</returns>
    public static List<TopoPoint> Simplify(
        List<TopoPoint> points,
        double tolerance)
    {
        if (points == null || points.Count <= 2)
            return points; // 少于3个点无需简化

        var keep = new bool[points.Count];
        for (int i = 0; i < keep.Length; i++) keep[i] = false;

        keep[0] = true;
        keep[points.Count - 1] = true;

        SimplifyRec(points, 0, points.Count - 1, tolerance, keep);

        return points.Where((pt, index) => keep[index]).ToList();
    }

    private static void SimplifyRec(
        List<TopoPoint> points,
        int start,
        int end,
        double tolerance,
        bool[] keep)
    {
        if (start + 1 >= end) return;

        // 找到距离起点-终点线段最远的点
        double maxDist = 0;
        int maxIndex = start;

        for (int i = start + 1; i < end; i++)
        {
            double dist = PerpendicularDistance(
                points[start].X, points[start].Y,
                points[end].X, points[end].Y,
                points[i].X, points[i].Y);

            if (dist > maxDist)
            {
                maxDist = dist;
                maxIndex = i;
            }
        }
        
        if (maxDist > tolerance)
        {
            keep[maxIndex] = true;
            SimplifyRec(points, start, maxIndex, tolerance, keep);
            SimplifyRec(points, maxIndex, end, tolerance, keep);
        }
    }

    /// <summary>
    /// 点到线段的垂直距离
    /// </summary>
    private static double PerpendicularDistance(
        double ax, double ay,
        double bx, double by,
        double px, double py)
    {
        double dx = bx - ax;
        double dy = by - ay;

        if (dx == 0 && dy == 0)
            return Math.Sqrt((px - ax) * (px - ax) + (py - ay) * (py - ay));

        // 距离公式:| (dy*px - dx*py + bx*ay - by*ax) | / sqrt(dx² + dy²)
        double numerator = Math.Abs(dy * px - dx * py + bx * ay - by * ax);
        double denominator = Math.Sqrt(dx * dx + dy * dy);

        return numerator / denominator;
    }
}
复制代码
### c++

```csharp
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>

// 定义点类型
using Point = std::pair<double, double>;
using PointList = std::vector<Point>;

/**
 * 计算点 p 到线段 (start -> end) 的垂直距离
 */
double PerpendicularDistance(
    const Point& start,
    const Point& end,
    const Point& p)
{
    double dx = end.first - start.first;
    double dy = end.second - start.second;

    // 如果线段退化为点
    if (std::abs(dx) < 1e-10 && std::abs(dy) < 1e-10)
    {
        double dx1 = p.first - start.first;
        double dy1 = p.second - start.second;
        return std::sqrt(dx1 * dx1 + dy1 * dy1);
    }

    // 距离公式:| (dy * px - dx * py + end.x * start.y - end.y * start.x) | / sqrt(dx² + dy²)
    double numerator = std::abs(dy * p.first - dx * p.second + end.first * start.second - end.second * start.first);
    double denominator = std::sqrt(dx * dx + dy * dy);

    return numerator / denominator;
}

/**
 * Douglas-Peucker 递归处理
 */
void DouglasPeuckerRecursive(
    const PointList& points,
    int startIdx,
    int endIdx,
    double tolerance,
    std::vector<bool>& keep)
{
    if (startIdx + 1 >= endIdx)
        return;

    // 找出距离线段最远的点
    double maxDistance = 0.0;
    int maxIndex = startIdx;

    for (int i = startIdx + 1; i < endIdx; ++i)
    {
        double dist = PerpendicularDistance(points[startIdx], points[endIdx], points[i]);
        if (dist > maxDistance)
        {
            maxDistance = dist;
            maxIndex = i;
        }
    }

    // 如果最远距离大于阈值,则保留该点并递归处理两侧
    if (maxDistance > tolerance)
    {
        keep[maxIndex] = true;
        DouglasPeuckerRecursive(points, startIdx, maxIndex, tolerance, keep);
        DouglasPeuckerRecursive(points, maxIndex, endIdx, tolerance, keep);
    }
}

/**
 * 简化点集合:Douglas-Peucker 算法主函数
 * @param points 原始点集(至少2个点)
 * @param tolerance 简化阈值(单位:mm 或脉冲),建议 0.01 ~ 0.5
 * @return 简化后的点集
 */
PointList SimplifyPoints(const PointList& points, double tolerance)
{
    int n = points.size();
    if (n <= 2)
        return points;  // 无需简化

    std::vector<bool> keep(n, false);
    keep[0] = true;
    keep[n - 1] = true;

    DouglasPeuckerRecursive(points, 0, n - 1, tolerance, keep);

    PointList result;
    for (int i = 0; i < n; ++i)
    {
        if (keep[i])
        {
            result.emplace_back(points[i]);
        }
    }

    return result;
}
相关推荐
scixing43 分钟前
函数式编程 第八讲 循环者,递归也
开发语言·c#
屠夫1 小时前
SqlSugar的简单使用
c#
dotent·17 小时前
C#基于WPF UI框架的通用基础上位机测试WPF框架
ui·c#·wpf
合作小小程序员小小店17 小时前
桌面开发,超市管理系统开发,基于C#,winform,sql server数据库
开发语言·数据库·sql·microsoft·sqlserver·c#
合作小小程序员小小店19 小时前
桌面开发,在线%超市销售管理%系统,基于vs2022,c#,winform,sql server数据
开发语言·数据库·microsoft·c#
p***323520 小时前
如何使用C#与SQL Server数据库进行交互
数据库·c#·交互
2501_941807261 天前
Java高性能消息队列与Kafka实战分享:大规模消息处理、异步通信与性能优化经验
c#·linq
周杰伦fans1 天前
C# 中的**享元工厂**模式
开发语言·数据库·c#