简化点集合 道格拉斯-普克算法(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;
}
相关推荐
典典分享指南2 小时前
一品牌多产品线的 GEO 内容矩阵:从策略到落地
java·c#·bash·symfony
今夜有雨.3 小时前
C# 学习文档(零基础入门)
开发语言·学习·c#
软件黑马王子3 小时前
14.事件中心:主要作用和原理
开发语言·前端框架·c#
海盗12348 小时前
微软技术周报 2026-08-31~09-07:GPT-6 Astra 登陆 Foundry、M365 全球认证故障复盘、C# 15 封闭层级
gpt·microsoft·c#
reasonsummer9 小时前
【办公类-115-01】20260906育儿知识(家园小报)批量制作(2026年9月-2027年6月)
开发语言·数据库·c#
软件黑马王子10 小时前
13.缓存池优化:对象上限配置
开发语言·前端框架·c#
唐青枫10 小时前
C#.NET StructureMap 从依赖注入到项目实战
c#·.net
fujisheng66111 小时前
FUI 编译期装配实践:从反射注册到 Source Generator
c#·unity3d
智码看视界11 小时前
.NET 10 推理大模型TensorSharp 3.3.0 部署实测:纯.NET推理引擎反超llama.cpp 1.5倍,DFlash2提速62%
c#·.net·llama.cpp·.net 10·tensorsharp·本地大模型推理·开源推理引擎
格林威11 小时前
C# 图像异步落盘存储:基于Channel 配合 ArrayPool 实现异步落盘
开发语言·人工智能·数码相机·机器学习·计算机视觉·c#·视觉检测