简化点集合 道格拉斯-普克算法(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;
}
相关推荐
2501_9307077811 小时前
使用C#代码更改 PowerPoint 幻灯片大小
开发语言·c#·powerpoint
Z_W_H_12 小时前
【C#】C#中值类型和引用类型参数传递的区别
开发语言·c#
用户83562907805113 小时前
使用 C# 高效解析 PDF 文档:文本与表格提取实战指南
后端·c#
rit843249914 小时前
C#实现的远程控制系统
前端·javascript·c#
wuguan_15 小时前
C#中的静态成员、常量和只读变量
开发语言·c#
秋雨雁南飞15 小时前
C# SignalR 添加Swagger
c#·swagger·signalr
张人玉15 小时前
C# 与西门子 PLC 通信:地址相关核心知识点
开发语言·microsoft·c#·plc
Yuuuuuuu16 小时前
WPF基于Canvas绘制多边形ROI
c#
缺点内向16 小时前
如何在 C# 中重命名 Excel 工作表并设置标签颜色
开发语言·c#·excel
a努力。17 小时前
网易Java面试被问:偏向锁在什么场景下反而降低性能?如何关闭?
java·开发语言·后端·面试·架构·c#