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. 数据交换准备:合并多段线以符合其他软件格式要求

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


参考来源

相关推荐
如果'\'真能转义说11 分钟前
OOXML 文档格式剖析:哈希、ZIP结构与识别
xml·算法·c#·哈希算法
我是唐青枫15 分钟前
终于不用手搓两级缓存了!C#.NET HybridCache 详解:L1 L2、标签失效与防击穿实战
redis·缓存·c#·.net
梦梦代码精2 小时前
BuildingAI 上部署自定义工作流智能体:5 个实用技巧
大数据·人工智能·算法·开源软件
Zephyr_03 小时前
Leedcode算法题
java·算法
流年如夢3 小时前
栈和列队(LeetCode)
数据结构·算法·leetcode·链表·职场和发展
Hello.Reader4 小时前
算法基础(十)——分治思想把大问题拆成小问题
java·开发语言·算法
CHANG_THE_WORLD5 小时前
C语言中的 %*s 和 %.*s 和C++的字符串格式化输出
c语言·c++·c#
绛橘色的日落(。・∀・)ノ5 小时前
机器学习之评估与偏差方差分析
算法
消失的旧时光-19435 小时前
C语言对象模型系列(四)《Linux 内核里的 container_of 到底是什么黑魔法?》—— 一篇讲透 Linux 内核的“对象模型”核心技巧
linux·c语言·算法