C#合并首尾相连多段线实战

问题解构

用户需要在C#中实现合并所有首尾相连的多段线。核心需求包括:

  1. 识别连接性:判断多段线是否首尾相连(端点重合)
  2. 合并算法:将相连的多段线合并为单一多段线
  3. CAD环境:通常在AutoCAD等CAD二次开发环境中操作

方案推演

基于CAD二次开发经验,合并首尾相连多段线的标准流程如下:

步骤 关键操作 技术要点
1. 收集多段线 获取所有待处理的多段线对象 通过选择集或遍历图元
2. 建立连接图 构建端点索引关系 使用字典存储端点坐标与多段线映射
3. 合并处理 递归合并相连多段线 处理顶点顺序和方向
4. 生成结果 创建新多段线并清理原对象 注意内存管理和事务提交

具体实现

1. 核心数据结构

csharp 复制代码
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using System.Collections.Generic;
using System.Linq;

public class PolylineMerger
{
    // 端点容差(用于判断是否重合)
    private const double Tolerance = 0.0001;
    
    // 端点信息结构
    private class EndpointInfo
    {
        public Point3d Point { get; set; }
        public Polyline Polyline { get; set; }
        public bool IsStartPoint { get; set; }
    }
}

2. 连接性检测方法

csharp 复制代码
private bool ArePointsEqual(Point3d p1, Point3d p2)
{
    return p1.DistanceTo(p2) < Tolerance;
}

private bool ArePolylinesConnected(Polyline pl1, Polyline pl2)
{
    Point3d start1 = pl1.StartPoint;
    Point3d end1 = pl1.EndPoint;
    Point3d start2 = pl2.StartPoint;
    Point3d end2 = pl2.EndPoint;
    
    // 判断四种可能的连接情况
    return ArePointsEqual(end1, start2) || 
           ArePointsEqual(end1, end2) ||
           ArePointsEqual(start1, start2) ||
           ArePointsEqual(start1, end2);
}

3. 多段线合并算法

csharp 复制代码
public List<Polyline> MergeConnectedPolylines(List<Polyline> polylines)
{
    if (polylines == null || polylines.Count < 2)
        return polylines;
    
    // 构建端点索引
    Dictionary<Point3d, List<EndpointInfo>> endpointMap = new Dictionary<Point3d, List<EndpointInfo>>();
    
    foreach (var pl in polylines)
    {
        AddEndpointToMap(pl.StartPoint, pl, true, endpointMap);
        AddEndpointToMap(pl.EndPoint, pl, false, endpointMap);
    }
    
    // 合并处理
    List<Polyline> result = new List<Polyline>();
    HashSet<Polyline> processed = new HashSet<Polyline>();
    
    foreach (var pl in polylines)
    {
        if (processed.Contains(pl)) continue;
        
        Polyline merged = MergeFromPolyline(pl, endpointMap, processed);
        if (merged != null)
        {
            result.Add(merged);
        }
    }
    
    return result;
}

private void AddEndpointToMap(Point3d point, Polyline pl, bool isStart, 
                             Dictionary<Point3d, List<EndpointInfo>> map)
{
    // 查找相近的端点
    Point3d? existingKey = null;
    foreach (var key in map.Keys)
    {
        if (ArePointsEqual(key, point))
        {
            existingKey = key;
            break;
        }
    }
    
    Point3d actualPoint = existingKey ?? point;
    if (!map.ContainsKey(actualPoint))
    {
        map[actualPoint] = new List<EndpointInfo>();
    }
    
    map[actualPoint].Add(new EndpointInfo 
    { 
        Point = actualPoint, 
        Polyline = pl, 
        IsStartPoint = isStart 
    });
}

4. 递归合并实现

csharp 复制代码
private Polyline MergeFromPolyline(Polyline startPl, 
                                  Dictionary<Point3d, List<EndpointInfo>> endpointMap,
                                  HashSet<Polyline> processed)
{
    List<Point3d> mergedPoints = new List<Point3d>();
    List<double> mergedBulges = new List<double>();
    
    // 收集起点多段线的顶点
    CollectVertices(startPl, mergedPoints, mergedBulges);
    processed.Add(startPl);
    
    // 递归查找并合并相连多段线
    bool foundMore = true;
    while (foundMore)
    {
        foundMore = false;
        Point3d currentEnd = mergedPoints.Last();
        
        if (endpointMap.ContainsKey(currentEnd))
        {
            foreach (var endpointInfo in endpointMap[currentEnd])
            {
                if (processed.Contains(endpointInfo.Polyline)) continue;
                
                // 检查连接方向
                if (ShouldConnect(currentEnd, endpointInfo))
                {
                    AppendPolylineVertices(endpointInfo.Polyline, endpointInfo.IsStartPoint, 
                                         mergedPoints, mergedBulges);
                    processed.Add(endpointInfo.Polyline);
                    foundMore = true;
                    break;
                }
            }
        }
    }
    
    // 创建新的合并后多段线
    return CreateMergedPolyline(mergedPoints, mergedBulges);
}

private void CollectVertices(Polyline pl, List<Point3d> points, List<double> bulges)
{
    for (int i = 0; i < pl.NumberOfVertices; i++)
    {
        points.Add(pl.GetPoint3dAt(i));
        if (i < pl.NumberOfVertices - 1)
        {
            bulges.Add(pl.GetBulgeAt(i));
        }
    }
}

5. 完整调用示例

csharp 复制代码
[CommandMethod("MergePolylines")]
public void MergePolylinesCommand()
{
    Document doc = Application.DocumentManager.MdiActiveDocument;
    Database db = doc.Database;
    Editor ed = doc.Editor;
    
    using (Transaction tr = db.TransactionManager.StartTransaction())
    {
        try
        {
            // 1. 选择多段线
            PromptSelectionResult selResult = ed.GetSelection();
            if (selResult.Status != PromptStatus.OK) return;
            
            // 2. 收集多段线对象
            List<Polyline> polylines = new List<Polyline>();
            foreach (SelectedObject selObj in selResult.Value)
            {
                Entity ent = tr.GetObject(selObj.ObjectId, OpenMode.ForRead) as Entity;
                if (ent is Polyline pl)
                {
                    polylines.Add(pl);
                }
            }
            
            // 3. 执行合并
            PolylineMerger merger = new PolylineMerger();
            List<Polyline> mergedPolylines = merger.MergeConnectedPolylines(polylines);
            
            // 4. 创建新实体并删除原实体
            BlockTableRecord btr = (BlockTableRecord)tr.GetObject(
                db.CurrentSpaceId, OpenMode.ForWrite);
            
            foreach (var mergedPl in mergedPolylines)
            {
                btr.AppendEntity(mergedPl);
                tr.AddNewlyCreatedDBObject(mergedPl, true);
            }
            
            // 可选:删除原多段线
            foreach (var pl in polylines)
            {
                pl.UpgradeOpen();
                pl.Erase();
            }
            
            tr.Commit();
            ed.WriteMessage($"
合并完成:{polylines.Count}条多段线合并为{mergedPolylines.Count}条");
        }
        catch (System.Exception ex)
        {
            ed.WriteMessage($"
错误:{ex.Message}");
        }
    }
}

关键注意事项

注意事项 解决方案 参考依据
端点容差处理 使用容差值比较坐标,避免浮点精度问题
顶点顺序保持 根据连接方向调整顶点添加顺序
凸度值传递 合并时保留圆弧段的凸度值
性能优化 使用字典建立端点索引,避免O(n²)复杂度 -
Z坐标处理 忽略Z坐标或保持一致性
闭合多段线 特殊处理闭合多段线的连接逻辑 -

应用场景示例

  1. CAD图纸清理:将破碎的线段合并为完整轮廓
  2. 激光切割路径优化:减少路径中断点,提高加工效率
  3. 工程图处理:简化复杂的多段线结构,便于后续操作
  4. 数据交换准备:合并多段线以符合其他软件格式要求

该实现通过建立端点索引图高效识别连接关系,递归合并确保处理任意长度的连接链,容差比较解决坐标精度问题,完整支持直线和圆弧段的合并需求。


参考来源

相关推荐
KMDxiaozuanfeng2 小时前
卡梅德生物技术快报|SPR 技术应用|基于 SPR 亲和力的中药活性成分筛选系统实现与数据分析
科技·算法·面试·考试
꧁细听勿语情꧂3 小时前
数据结构概念和算法、时间复杂度、空间复杂度引入
c语言·开发语言·数据结构·算法
Felven3 小时前
B. The 67th 6-7 Integer Problem
数据结构·算法
玉树临风ives3 小时前
atcoder ABC 454 题解
算法·深度优先·图论
钮钴禄·爱因斯晨3 小时前
聚焦操作系统中的PV操作
数据库·算法·系统架构·c#
云泽8083 小时前
笔试算法 - 双指针篇(一):移动零、复写零、快乐数与盛水容器
c++·算法
willhuo3 小时前
# 自动化数据采集技术研究与实现:基于Playwright的抖音网页自动化方案
运维·selenium·c#·自动化·chrome devtools·webview
不才小强4 小时前
目标跟踪算法DeepSort实战
人工智能·算法·目标跟踪
papership4 小时前
【入门级-数学与其他:1.数及其运算:进制与进制转换:二进制、八进制、十进制、十六进制】
算法