using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using System;
using System.Collections.Generic;
using System.Linq;
namespace FixedOverkillPlugin
{
public class Commands
{
// 配置参数
public class OverkillOptions
{
public double Tolerance { get; set; } = 0.1;
public bool IgnoreColor { get; set; } = true;
public bool IgnoreLayer { get; set; } = true;
public bool IgnoreLinetype { get; set; } = true;
public bool OptimizePolylines { get; set; } = true;
public bool MergeOverlapping { get; set; } = true;
public bool MergeCollinear { get; set; } = true;
public bool PreserveAssociativity { get; set; } = true;
}
[CommandMethod("MG", CommandFlags.Modal)]
public void MergeLines()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
try
{
// 获取用户选项
OverkillOptions options = GetUserOptions(ed);
if (options == null) return;
// 获取选择集
PromptSelectionResult selResult = ed.GetSelection();
if (selResult.Status != PromptStatus.OK) return;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
// 收集所有线段
var objectsToErase = new HashSet<ObjectId>();
var allSegments = new List<LineSegment>();
// 收集所有线段
CollectAllSegments(selResult, tr, options, allSegments, objectsToErase, ed);
// 删除原始对象
DeleteOriginalObjects(tr, objectsToErase, ed);
// 处理线段
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(
db.CurrentSpaceId, OpenMode.ForWrite);
// 合并线段
List<Line> mergedLines = MergeSegments(allSegments, options, ed);
// 添加新实体
foreach (Line newLine in mergedLines)
{
btr.AppendEntity(newLine);
tr.AddNewlyCreatedDBObject(newLine, true);
}
tr.Commit();
}
ed.WriteMessage(Environment.NewLine + "线段合并完成!");
}
catch (System.Exception ex)
{
ed.WriteMessage(Environment.NewLine + $"错误: {ex.Message}" + Environment.NewLine + $"{ex.StackTrace}");
}
}
// 获取用户选项(修复了关键字设置问题)
private OverkillOptions GetUserOptions(Editor ed)
{
OverkillOptions options = new OverkillOptions();
// 创建关键字选项
var kws = new List<Tuple<string, string, string>>();
kws.Add(Tuple.Create("Tolerance", "公差(T)", "公差(T)"));
kws.Add(Tuple.Create("Ignore", "忽略特性(I)", "忽略特性(I)"));
kws.Add(Tuple.Create("Optimize", "优化选项(O)", "优化选项(O)"));
kws.Add(Tuple.Create("Accept", "接受", "接受"));
while (true)
{
PromptKeywordOptions pko = new PromptKeywordOptions(
Environment.NewLine + "设置选项 [公差(T)/忽略特性(I)/优化选项(O)/接受(A)]: ");
// 添加关键字选项
foreach (var kw in kws)
{
pko.Keywords.Add(kw.Item1, kw.Item2, kw.Item3);
}
pko.AllowNone = true;
PromptResult pr = ed.GetKeywords(pko);
if (pr.Status != PromptStatus.OK) return null;
switch (pr.StringResult)
{
case "Tolerance":
PromptDoubleOptions pdo = new PromptDoubleOptions(
Environment.NewLine + $"输入公差值 <{options.Tolerance}>: ");
pdo.DefaultValue = options.Tolerance;
pdo.AllowNegative = false;
pdo.AllowZero = false;
PromptDoubleResult pdr = ed.GetDouble(pdo);
if (pdr.Status == PromptStatus.OK)
{
options.Tolerance = pdr.Value;
}
break;
case "Ignore":
var iko = new PromptKeywordOptions(
Environment.NewLine + "选择忽略的特性 [颜色(C)/图层(LA)/线型(LT)/全部(A)/无(N)]: ");
iko.Keywords.Add("Color", "颜色(C)", "颜色(C)");
iko.Keywords.Add("Layer", "图层(LA)", "图层(LA)");
iko.Keywords.Add("Linetype", "线型(LT)", "线型(LT)");
iko.Keywords.Add("All", "全部(A)", "全部(A)");
iko.Keywords.Add("None", "无(N)", "无(N)");
PromptResult ikr = ed.GetKeywords(iko);
if (ikr.Status == PromptStatus.OK)
{
switch (ikr.StringResult)
{
case "Color":
options.IgnoreColor = !options.IgnoreColor;
break;
case "Layer":
options.IgnoreLayer = !options.IgnoreLayer;
break;
case "Linetype":
options.IgnoreLinetype = !options.IgnoreLinetype;
break;
case "All":
options.IgnoreColor = true;
options.IgnoreLayer = true;
options.IgnoreLinetype = true;
break;
case "None":
options.IgnoreColor = false;
options.IgnoreLayer = false;
options.IgnoreLinetype = false;
break;
}
}
break;
case "Optimize":
var oko = new PromptKeywordOptions(
Environment.NewLine + "选择优化选项 [优化多段线(P)/合并重叠(O)/合并共线(C)/保持关联(K)]: ");
oko.Keywords.Add("Polyline", "优化多段线(P)", "优化多段线(P)");
oko.Keywords.Add("Overlap", "合并重叠(O)", "合并重叠(O)");
oko.Keywords.Add("Collinear", "合并共线(C)", "合并共线(C)");
oko.Keywords.Add("Associativity", "保持关联(K)", "保持关联(K)");
PromptResult okr = ed.GetKeywords(oko);
if (okr.Status == PromptStatus.OK)
{
switch (okr.StringResult)
{
case "Polyline":
options.OptimizePolylines = !options.OptimizePolylines;
break;
case "Overlap":
options.MergeOverlapping = !options.MergeOverlapping;
break;
case "Collinear":
options.MergeCollinear = !options.MergeCollinear;
break;
case "Associativity":
options.PreserveAssociativity = !options.PreserveAssociativity;
break;
}
}
break;
case "Accept":
return options;
}
// 显示当前设置
ed.WriteMessage(Environment.NewLine + $"当前设置: 公差={options.Tolerance}, " +
$"忽略特性[颜色={options.IgnoreColor}, 图层={options.IgnoreLayer}, 线型={options.IgnoreLinetype}], " +
$"优化选项[多段线={options.OptimizePolylines}, 重叠={options.MergeOverlapping}, " +
$"共线={options.MergeCollinear}, 关联={options.PreserveAssociativity}]");
}
}
// 线段表示类
public class LineSegment
{
public Point3d Start { get; set; }
public Point3d End { get; set; }
public ObjectId OriginalId { get; set; } = ObjectId.Null;
public string Layer { get; set; }
public string Linetype { get; set; }
public int Color { get; set; }
public bool IsPolylineSegment { get; set; }
public LineSegment(Point3d start, Point3d end)
{
Start = start;
End = end;
}
public Extents3d Extents => new Extents3d(Start, End);
}
// 收集所有线段
private void CollectAllSegments(PromptSelectionResult selResult, Transaction tr,
OverkillOptions options, List<LineSegment> allSegments,
HashSet<ObjectId> objectsToErase, Editor ed)
{
int totalObjects = selResult.Value.Count;
int processed = 0;
foreach (SelectedObject selObj in selResult.Value)
{
processed++;
if (processed % 100 == 0)
ed.WriteMessage(Environment.NewLine + $"已处理: {processed}/{totalObjects}");
Entity ent = tr.GetObject(selObj.ObjectId, OpenMode.ForRead) as Entity;
if (ent == null) continue;
// 记录要删除的对象
objectsToErase.Add(ent.ObjectId);
// 处理直线
if (ent is Line line)
{
allSegments.Add(new LineSegment(line.StartPoint, line.EndPoint)
{
OriginalId = line.ObjectId,
Layer = options.IgnoreLayer ? "" : line.Layer,
Linetype = options.IgnoreLinetype ? "" : line.Linetype,
Color = options.IgnoreColor ? 0 : line.ColorIndex
});
}
// 处理多段线
else if (ent is Polyline pline && options.OptimizePolylines)
{
for (int i = 0; i < pline.NumberOfVertices - 1; i++)
{
allSegments.Add(new LineSegment(
pline.GetPoint3dAt(i),
pline.GetPoint3dAt(i + 1)
)
{
OriginalId = pline.ObjectId,
Layer = options.IgnoreLayer ? "" : pline.Layer,
Linetype = options.IgnoreLinetype ? "" : pline.Linetype,
Color = options.IgnoreColor ? 0 : pline.ColorIndex,
IsPolylineSegment = true
});
}
}
// 处理其他实体
else if (options.PreserveAssociativity)
{
objectsToErase.Remove(ent.ObjectId);
}
}
}
// 删除原始对象
private void DeleteOriginalObjects(Transaction tr, HashSet<ObjectId> objectsToErase, Editor ed)
{
int total = objectsToErase.Count;
int processed = 0;
int deletedCount = 0;
foreach (ObjectId id in objectsToErase)
{
processed++;
if (processed % 100 == 0)
ed.WriteMessage(Environment.NewLine + $"删除对象: {processed}/{total}");
if (id.IsValid && !id.IsErased && id.IsResident)
{
try
{
DBObject obj = tr.GetObject(id, OpenMode.ForWrite);
if (obj != null)
{
obj.Erase();
deletedCount++;
}
}
catch { /* 忽略错误 */ }
}
}
ed.WriteMessage(Environment.NewLine + $"成功删除 {deletedCount} 个对象");
}
// 高级线段合并算法
private List<Line> MergeSegments(List<LineSegment> segments, OverkillOptions options, Editor ed)
{
if (segments.Count == 0) return new List<Line>();
ed.WriteMessage(Environment.NewLine + $"开始合并线段: {segments.Count} 条");
// 分组
var groupedSegments = segments
.GroupBy(s => new {
Layer = options.IgnoreLayer ? "" : s.Layer,
Linetype = options.IgnoreLinetype ? "" : s.Linetype,
Color = options.IgnoreColor ? 0 : s.Color
})
.ToList();
List<Line> result = new List<Line>();
int groupCount = 0;
foreach (var group in groupedSegments)
{
groupCount++;
ed.WriteMessage(Environment.NewLine + $"处理组 {groupCount}/{groupedSegments.Count}: " +
$"图层={group.Key.Layer}, 线型={group.Key.Linetype}, 颜色={group.Key.Color}");
// 步骤1:打断重叠线段
List<LineSegment> brokenSegments = options.MergeOverlapping ?
BreakOverlappingSegments(group.ToList(), options.Tolerance, ed) :
group.ToList();
// 步骤2:合并共线线段
List<LineSegment> mergedSegments = options.MergeCollinear ?
MergeCollinearSegments(brokenSegments, options.Tolerance, ed) :
brokenSegments;
// 转换为Line对象
foreach (var seg in mergedSegments)
{
Line newLine = new Line(seg.Start, seg.End);
newLine.Layer = group.Key.Layer;
newLine.Linetype = group.Key.Linetype;
newLine.ColorIndex = group.Key.Color;
result.Add(newLine);
}
}
ed.WriteMessage(Environment.NewLine + $"合并完成: 原始 {segments.Count} 条 → 合并 {result.Count} 条");
return result;
}
// 打断重叠线段
private List<LineSegment> BreakOverlappingSegments(List<LineSegment> segments, double tolerance, Editor ed)
{
if (segments.Count < 2) return segments;
// 使用空间索引加速
SpatialIndex index = BuildSpatialIndex(segments, tolerance);
List<LineSegment> result = new List<LineSegment>();
HashSet<LineSegment> processed = new HashSet<LineSegment>();
int total = segments.Count;
int processedCount = 0;
foreach (LineSegment seg in segments)
{
processedCount++;
if (processedCount % 100 == 0)
ed.WriteMessage(Environment.NewLine + $"打断重叠: {processedCount}/{total}");
if (processed.Contains(seg)) continue;
List<LineSegment> currentSegments = new List<LineSegment> { seg };
bool modified;
do
{
modified = false;
List<LineSegment> nextSegments = new List<LineSegment>();
foreach (LineSegment current in currentSegments)
{
// 查找可能重叠的线段
var candidates = index.Search(current.Extents)
.Where(s => !processed.Contains(s) && s != current)
.ToList();
bool broken = false;
foreach (var candidate in candidates)
{
if (AreSegmentsOverlapping(current, candidate, tolerance))
{
List<LineSegment> newSegments = BreakOverlappingPair(current, candidate, tolerance);
nextSegments.AddRange(newSegments);
processed.Add(candidate);
broken = true;
break;
}
}
if (!broken)
{
nextSegments.Add(current);
}
else
{
modified = true;
}
}
currentSegments = nextSegments;
} while (modified);
result.AddRange(currentSegments);
processed.Add(seg);
}
return result;
}
// 检查线段是否重叠
private bool AreSegmentsOverlapping(LineSegment seg1, LineSegment seg2, double tolerance)
{
// 创建带公差的线段
Line line1 = new Line(seg1.Start, seg1.End);
Line line2 = new Line(seg2.Start, seg2.End);
// 计算方向向量
Vector3d dir1 = seg1.End - seg1.Start;
Vector3d dir2 = seg2.End - seg2.Start;
// 检查是否平行
if (!dir1.IsParallelTo(dir2, new Tolerance(tolerance, tolerance)))
return false;
// 检查投影重叠
double min1 = Math.Min(0, dir1.Length);
double max1 = Math.Max(0, dir1.Length);
double min2 = Math.Min(0, dir2.Length);
double max2 = Math.Max(0, dir2.Length);
double proj1 = ProjectPointOnLine(seg2.Start, seg1.Start, dir1, tolerance);
double proj2 = ProjectPointOnLine(seg2.End, seg1.Start, dir1, tolerance);
double seg2Min = Math.Min(proj1, proj2);
double seg2Max = Math.Max(proj1, proj2);
// 检查重叠
return (min1 <= seg2Max + tolerance && max1 >= seg2Min - tolerance) ||
(seg2Min <= max1 + tolerance && seg2Max >= min1 - tolerance);
}
// 投影点到线段
private double ProjectPointOnLine(Point3d point, Point3d lineStart, Vector3d direction, double tolerance)
{
Vector3d v = point - lineStart;
return v.DotProduct(direction) / direction.Length;
}
// 打断重叠线段对
private List<LineSegment> BreakOverlappingPair(LineSegment seg1, LineSegment seg2, double tolerance)
{
// 计算投影点
Vector3d dir = seg1.End - seg1.Start;
double projStart = ProjectPointOnLine(seg2.Start, seg1.Start, dir, tolerance);
double projEnd = ProjectPointOnLine(seg2.End, seg1.Start, dir, tolerance);
// 计算实际点
Point3d p1 = seg1.Start + dir * (projStart / dir.Length);
Point3d p2 = seg1.Start + dir * (projEnd / dir.Length);
// 创建分割点
List<double> splitParams = new List<double>();
splitParams.Add(0); // 起点
splitParams.Add(dir.Length); // 终点
if (projStart > 0 && projStart < dir.Length)
splitParams.Add(projStart);
if (projEnd > 0 && projEnd < dir.Length)
splitParams.Add(projEnd);
splitParams.Sort();
// 创建新线段
List<LineSegment> result = new List<LineSegment>();
for (int i = 0; i < splitParams.Count - 1; i++)
{
double startParam = splitParams[i];
double endParam = splitParams[i + 1];
// 跳过太小的线段
if (endParam - startParam < tolerance) continue;
Point3d startPt = seg1.Start + dir * (startParam / dir.Length);
Point3d endPt = seg1.Start + dir * (endParam / dir.Length);
result.Add(new LineSegment(startPt, endPt));
}
return result;
}
// 合并共线线段
private List<LineSegment> MergeCollinearSegments(List<LineSegment> segments, double tolerance, Editor ed)
{
if (segments.Count == 0) return segments;
// 按起点排序
List<LineSegment> sortedSegments = segments
.OrderBy(s => s.Start.X)
.ThenBy(s => s.Start.Y)
.ThenBy(s => s.Start.Z)
.ToList();
// 使用并查集合并
UnionFind uf = new UnionFind(sortedSegments.Count);
// 构建空间索引
SpatialIndex index = BuildSpatialIndex(sortedSegments, tolerance);
for (int i = 0; i < sortedSegments.Count; i++)
{
LineSegment seg1 = sortedSegments[i];
// 查找附近线段
var candidates = index.Search(seg1.Extents)
.Where((s, idx) => idx > i) // 只检查后面的线段
.ToList();
foreach (var candidate in candidates)
{
int j = sortedSegments.IndexOf(candidate);
if (j <= i) continue;
if (AreSegmentsConnectable(seg1, candidate, tolerance))
{
uf.Union(i, j);
}
}
}
// 分组合并
Dictionary<int, List<LineSegment>> groups = new Dictionary<int, List<LineSegment>>();
for (int i = 0; i < sortedSegments.Count; i++)
{
int root = uf.Find(i);
if (!groups.ContainsKey(root))
groups[root] = new List<LineSegment>();
groups[root].Add(sortedSegments[i]);
}
// 合并每组线段
List<LineSegment> result = new List<LineSegment>();
foreach (var group in groups.Values)
{
if (group.Count == 1)
{
result.Add(group[0]);
continue;
}
// 计算合并后的线段
Point3d minPoint = group[0].Start;
Point3d maxPoint = group[0].End;
foreach (var seg in group)
{
minPoint = PointMin(minPoint, PointMin(seg.Start, seg.End));
maxPoint = PointMax(maxPoint, PointMax(seg.Start, seg.End));
}
result.Add(new LineSegment(minPoint, maxPoint));
}
return result;
}
// 检查线段是否可连接
private bool AreSegmentsConnectable(LineSegment seg1, LineSegment seg2, double tolerance)
{
Vector3d dir1 = seg1.End - seg1.Start;
Vector3d dir2 = seg2.End - seg2.Start;
// 检查方向平行
if (!dir1.IsParallelTo(dir2, new Tolerance(tolerance, tolerance)))
return false;
// 检查端点连接
Tolerance tol = new Tolerance(tolerance, tolerance);
return
seg1.End.IsEqualTo(seg2.Start, tol) ||
seg1.Start.IsEqualTo(seg2.End, tol) ||
seg1.End.IsEqualTo(seg2.End, tol) ||
seg1.Start.IsEqualTo(seg2.Start, tol);
}
// 辅助方法:获取最小点
private Point3d PointMin(Point3d a, Point3d b)
{
return new Point3d(
Math.Min(a.X, b.X),
Math.Min(a.Y, b.Y),
Math.Min(a.Z, b.Z));
}
// 辅助方法:获取最大点
private Point3d PointMax(Point3d a, Point3d b)
{
return new Point3d(
Math.Max(a.X, b.X),
Math.Max(a.Y, b.Y),
Math.Max(a.Z, b.Z));
}
// 构建空间索引
private SpatialIndex BuildSpatialIndex(List<LineSegment> segments, double tolerance)
{
SpatialIndex index = new SpatialIndex();
foreach (LineSegment seg in segments)
{
// 正确创建扩展后的边界框
Point3d min = seg.Extents.MinPoint;
Point3d max = seg.Extents.MaxPoint;
// 创建新的扩展边界框
Point3d newMin = new Point3d(
min.X - tolerance,
min.Y - tolerance,
min.Z - tolerance);
Point3d newMax = new Point3d(
max.X + tolerance,
max.Y + tolerance,
max.Z + tolerance);
// 使用新的Extents3d对象
Extents3d extendedExt = new Extents3d(newMin, newMax);
index.Add(extendedExt, seg);
}
return index;
}
}
// 空间索引实现
public class SpatialIndex
{
private List<Tuple<Extents3d, Commands.LineSegment>> _items =
new List<Tuple<Extents3d, Commands.LineSegment>>();
public void Add(Extents3d extents, Commands.LineSegment segment)
{
_items.Add(new Tuple<Extents3d, Commands.LineSegment>(extents, segment));
}
public IEnumerable<Commands.LineSegment> Search(Extents3d searchExtents)
{
return _items
.Where(item => item.Item1.MinPoint.X <= searchExtents.MaxPoint.X &&
item.Item1.MaxPoint.X >= searchExtents.MinPoint.X &&
item.Item1.MinPoint.Y <= searchExtents.MaxPoint.Y &&
item.Item1.MaxPoint.Y >= searchExtents.MinPoint.Y)
.Select(item => item.Item2);
}
}
// 并查集实现
public class UnionFind
{
private int[] parent;
private int[] rank;
public UnionFind(int size)
{
parent = new int[size];
rank = new int[size];
for (int i = 0; i < size; i++)
{
parent[i] = i;
rank[i] = 0;
}
}
public int Find(int x)
{
if (parent[x] != x)
{
parent[x] = Find(parent[x]);
}
return parent[x];
}
public void Union(int x, int y)
{
int rootX = Find(x);
int rootY = Find(y);
if (rootX == rootY) return;
if (rank[rootX] < rank[rootY])
{
parent[rootX] = rootY;
}
else if (rank[rootX] > rank[rootY])
{
parent[rootY] = rootX;
}
else
{
parent[rootY] = rootX;
rank[rootX]++;
}
}
}
}
命令: MG
设置选项 [公差(T)/忽略特性(I)/优化选项(O)/接受(A)] [公差(T)/忽略特性(I)/优化选项(O)/接受]:
命令: MG
设置选项 [公差(T)/忽略特性(I)/优化选项(O)/接受(A)] [公差(T)/忽略特性(I)/优化选项(O)/接受]: A
无效的选项关键字。
设置选项 [公差(T)/忽略特性(I)/优化选项(O)/接受(A)] [公差(T)/忽略特性(I)/优化选项(O)/接受]: T
无效的选项关键字。
设置选项 [公差(T)/忽略特性(I)/优化选项(O)/接受(A)] [公差(T)/忽略特性(I)/优化选项(O)/接受]: I
无效的选项关键字。
最新发布