当前位置: 首页 > news >正文

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

问题解构

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

  1. 识别连接性:判断多段线是否首尾相连(端点重合)
  2. 合并算法:将相连的多段线合并为单一多段线
  3. 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 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. 递归合并实现

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. 完整调用示例

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

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


参考来源

  • cad二次开发 合并直线、圆弧、多段线
  • C# WPF界面源码框架:运动控制路径算法总结与分享
  • CAXA实体设计从零基础到高级应用实战教程
http://www.jsqmd.com/news/674473/

相关文章:

  • 基于TMS320F28035的汇川变频器源码:MD290、MD380、MD500三种型号及新的...
  • jQuery 遍历 - 祖先
  • 博通(Broadcom)数据中心交换机芯片的介绍
  • 鸿蒙市场份额飙升但国产厂商仍观望,生态差距与商业考量成阻碍
  • 22.React.js 中所谓的 “Pure Component” 是怎样的一类组件?
  • Spring Cloud Eureka停更后,我们团队是如何平滑迁移到Nacos的(附完整配置对比)
  • 极域电子教室2015版虚拟机环境搭建全流程(附Windows Server 2003镜像)
  • 从AT24C02到BMP280:手把手教你用STM32 HAL库玩转IIC,避开那些新手必踩的坑
  • 从Date到LocalDateTime:一次搞懂Java 8日期API的升级逻辑与实战迁移
  • 保姆级教程:用STM32和飞特STS3215舵机做个机械臂关节(附完整代码与协议解析)
  • 8Mb高速低功耗串行SPI SRAM嵌入式应用
  • YOLOFuse功能体验:多种融合策略,满足不同精度需求
  • 全球半导体展哪家好?2026年优质展会对比甄选顶级平台 - 品牌2026
  • 解锁BilibiliDown的5大隐藏功能:从基础下载到批量管理的完整探索指南
  • 3分钟永久激活Windows和Office:KMS_VL_ALL_AIO智能脚本终极指南
  • RMBG-1.4与Anaconda集成:Python数据科学工作流
  • 【Dify 2026多模态集成权威指南】:涵盖图像/语音/文本联合推理的7大实战陷阱与3步零代码接入法
  • 适合放在简历上的开源项目与练手项目Idea清单
  • 新手初步学习Java——从c语言到Java
  • QQ空间说说备份神器:GetQzonehistory完整使用指南
  • CSS如何创建三角箭头图标_通过border透明技巧实现
  • 【CTF那些事儿】ascii.txt
  • ARM地址转换与分支记录缓冲区(BRB)机制详解
  • GitX智能版本控制助手:告别Git命令行,让版本控制更高效
  • 3、IoT物理极限架构最佳实践:一文讲透端边双主(可分可合,非传统高可用)
  • HTML函数在旧版Windows跑得动吗_系统版本与硬件协同影响【指南】
  • HTML5中Canvas模拟物理重力与碰撞反弹的逻辑
  • 因漏洞数量激增,NIST 已停止对低优先级漏洞的评分
  • 摄影入门 | 从光到电:数码相机的成像核心
  • 【CTF那些事儿】b64steg.txt