问题解构用户需要在C#中实现合并所有首尾相连的多段线。核心需求包括识别连接性判断多段线是否首尾相连端点重合合并算法将相连的多段线合并为单一多段线CAD环境通常在AutoCAD等CAD二次开发环境中操作方案推演基于CAD二次开发经验合并首尾相连多段线的标准流程如下步骤关键操作技术要点1. 收集多段线获取所有待处理的多段线对象通过选择集或遍历图元2. 建立连接图构建端点索引关系使用字典存储端点坐标与多段线映射3. 合并处理递归合并相连多段线处理顶点顺序和方向4. 生成结果创建新多段线并清理原对象注意内存管理和事务提交具体实现1. 核心数据结构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. 连接性检测方法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. 多段线合并算法public ListPolyline MergeConnectedPolylines(ListPolyline polylines) { if (polylines null || polylines.Count 2) return polylines; // 构建端点索引 DictionaryPoint3d, ListEndpointInfo endpointMap new DictionaryPoint3d, ListEndpointInfo(); foreach (var pl in polylines) { AddEndpointToMap(pl.StartPoint, pl, true, endpointMap); AddEndpointToMap(pl.EndPoint, pl, false, endpointMap); } // 合并处理 ListPolyline result new ListPolyline(); HashSetPolyline processed new HashSetPolyline(); 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, DictionaryPoint3d, ListEndpointInfo 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 ListEndpointInfo(); } map[actualPoint].Add(new EndpointInfo { Point actualPoint, Polyline pl, IsStartPoint isStart }); }4. 递归合并实现private Polyline MergeFromPolyline(Polyline startPl, DictionaryPoint3d, ListEndpointInfo endpointMap, HashSetPolyline processed) { ListPoint3d mergedPoints new ListPoint3d(); Listdouble mergedBulges new Listdouble(); // 收集起点多段线的顶点 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, ListPoint3d points, Listdouble 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. 完整调用示例[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. 收集多段线对象 ListPolyline polylines new ListPolyline(); 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(); ListPolyline 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坐标或保持一致性闭合多段线特殊处理闭合多段线的连接逻辑-应用场景示例CAD图纸清理将破碎的线段合并为完整轮廓激光切割路径优化减少路径中断点提高加工效率工程图处理简化复杂的多段线结构便于后续操作数据交换准备合并多段线以符合其他软件格式要求该实现通过建立端点索引图高效识别连接关系递归合并确保处理任意长度的连接链容差比较解决坐标精度问题完整支持直线和圆弧段的合并需求。参考来源cad二次开发 合并直线、圆弧、多段线C# WPF界面源码框架运动控制路径算法总结与分享CAXA实体设计从零基础到高级应用实战教程