using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Colors;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
namespace ScaffoldPlugin
{
public class ScaffoldCommands
{
// 配置参数
private static readonly double[] BarLengths = { 300, 600, 900, 1200, 1500, 1800 };
private static readonly string BlockDirectory = Path.Combine(
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
“ScaffoldBlocks”);
// 容差参数 private const double ToleranceValue = 0.1; // 几何计算容差 private const double SpacingTolerance = 150.0; // 横杆长度匹配容差 private const double MinAxisLength = 100.0; // 最小轴网线长度 private const double MinDistanceForBar = 50.0; // 最小横杆间距 private const double MaxGapFactor = 1.2; // 最大间隙因子 // 块名称定义 private const string PoleBlockName = "ScaffoldPole立杆"; private const string BarPrefix = "ScaffoldPole横杆"; // 图层定义 private const string PoleLayer = "盘扣-立杆"; private const string BarLayer = "盘扣-横杆"; private const string AxisLayer = "盘扣轴网"; private const string DebugLayer = "盘扣-调试"; // 调试用图层 // 主命令:布置脚手架 [CommandMethod("BuildPlatform", "BP", CommandFlags.Modal)] public void BuildPlatform() { Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; try { // 检查块目录 if (!Directory.Exists(BlockDirectory)) { Directory.CreateDirectory(BlockDirectory); ShowErrorDialog($"块目录已创建: {BlockDirectory}\n请添加块文件后重新运行命令"); return; } // 创建所需图层 CreateLayerIfNotExists(db, PoleLayer, 7); // 红色 CreateLayerIfNotExists(db, BarLayer, 1); // 红色 CreateLayerIfNotExists(db, AxisLayer, 3); // 绿色 CreateLayerIfNotExists(db, DebugLayer, 2); // 黄色调试层 // 选择轴网线 var axisLineIds = SelectAxisLines(ed); if (axisLineIds == null || axisLineIds.Count == 0) { ShowErrorDialog("未选择轴网线或操作已取消!"); return; } // 获取轴网线对象 List<Line> axisLines = GetAxisLinesFromIds(db, axisLineIds); if (axisLines.Count == 0) { ShowErrorDialog("未找到有效轴网线!"); return; } // 计算有效交点(只在线段延伸相交处) ed.WriteMessage("\n正在计算有效交点..."); List<Point3d> validPoints = GetValidIntersectionPoints(axisLines); if (validPoints.Count == 0) { ShowErrorDialog("未找到有效交点!"); return; } ed.WriteMessage($"\n找到 {validPoints.Count} 个有效交点"); // 加载块定义 ed.WriteMessage("\n正在加载块定义..."); if (!LoadBlockDefinitions(db, ed)) return; // 清理现有脚手架 ed.WriteMessage("\n开始清理重叠区域的现有脚手架..."); CleanOverlappingScaffold(db, validPoints, ed); // 布置立杆(只在有效交点) ed.WriteMessage("\n开始布置立杆..."); int poleCount = PlacePoles(db, validPoints); ed.WriteMessage($"\n已布置 {poleCount} 根立杆"); // 布置横杆(只在连续线段) ed.WriteMessage("\n开始布置横杆..."); int barCount = PlaceBars(db, validPoints, axisLines, ed); ed.WriteMessage($"\n已布置 {barCount} 根横杆"); // 结果反馈 if (poleCount == 0 && barCount == 0) { ShowErrorDialog("脚手架布置失败,未布置任何构件!"); } else { Application.ShowAlertDialog($"成功布置 {poleCount}根立杆和{barCount}根横杆!"); } } catch (System.Exception ex) { ShowErrorDialog($"发生错误: {ex.Message}\n详细请查看命令行日志"); ed.WriteMessage($"\n错误: {ex.Message}\n{ex.StackTrace}"); } } // 选择轴网线 private List<ObjectId> SelectAxisLines(Editor ed) { var lineIds = new List<ObjectId>(); var filter = new SelectionFilter(new[] { new TypedValue(0, "LINE"), new TypedValue(8, AxisLayer) }); var opts = new PromptSelectionOptions { MessageForAdding = "\n选择盘扣轴网线 (按Enter结束选择): ", MessageForRemoval = "\n移除不需要的轴网线: ", AllowDuplicates = false, RejectObjectsOnLockedLayers = true }; PromptSelectionResult selection = ed.GetSelection(opts, filter); if (selection.Status != PromptStatus.OK) { ed.WriteMessage("\n未选择任何轴网线或选择已取消"); return lineIds; } using (var trans = ed.Document.TransactionManager.StartTransaction()) { foreach (ObjectId id in selection.Value.GetObjectIds()) { if (id.ObjectClass.DxfName == "LINE") { Entity entity = trans.GetObject(id, OpenMode.ForRead) as Entity; if (entity != null && entity.Layer == AxisLayer) { if (entity is Line line && line.Length > MinAxisLength) { lineIds.Add(id); } } } } trans.Commit(); } ed.WriteMessage($"\n已选择 {lineIds.Count} 条有效轴网线"); return lineIds; } // 从ID获取轴网线对象 private List<Line> GetAxisLinesFromIds(Database db, List<ObjectId> lineIds) { List<Line> lines = new List<Line>(); using (Transaction tr = db.TransactionManager.StartTransaction()) { foreach (ObjectId id in lineIds) { Line line = tr.GetObject(id, OpenMode.ForRead) as Line; if (line != null && line.Length > MinAxisLength) { lines.Add(line.Clone() as Line); // 克隆以避免原始对象修改 } } tr.Commit(); } return lines; } // 计算有效交点 private List<Point3d> GetValidIntersectionPoints(List<Line> axisLines) { List<Point3d> validPoints = new List<Point3d>(); for (int i = 0; i < axisLines.Count; i++) { for (int j = i + 1; j < axisLines.Count; j++) { Line line1 = axisLines[i]; Line line2 = axisLines[j]; Point3dCollection intersections = new Point3dCollection(); line1.IntersectWith(line2, Intersect.ExtendBoth, intersections, IntPtr.Zero, IntPtr.Zero); foreach (Point3d pt in intersections) { if (IsValidIntersection(pt, line1, line2)) { validPoints.Add(pt); } } } } // 移除重复点 return validPoints.Distinct(new Point3dComparer(ToleranceValue)).ToList(); } // 点比较器(考虑容差) private class Point3dComparer : IEqualityComparer<Point3d> { private readonly double _tolerance; public Point3dComparer(double tolerance) { _tolerance = tolerance; } public bool Equals(Point3d p1, Point3d p2) { return p1.DistanceTo(p2) < _tolerance; } public int GetHashCode(Point3d p) { return p.X.GetHashCode() ^ p.Y.GetHashCode() ^ p.Z.GetHashCode(); } } // 检查是否为有效交点 private bool IsValidIntersection(Point3d pt, Line line1, Line line2) { return IsPointOnLineSegment(pt, line1, ToleranceValue) && IsPointOnLineSegment(pt, line2, ToleranceValue); } // 检查点是否在线段范围内 private bool IsPointOnLineSegment(Point3d pt, Line line, double tolerance) { Vector3d lineVec = line.EndPoint - line.StartPoint; Vector3d startToPt = pt - line.StartPoint; double lineLengthSquared = lineVec.DotProduct(lineVec); double dotProduct = lineVec.DotProduct(startToPt); return dotProduct >= -tolerance && dotProduct <= lineLengthSquared + tolerance; } // 加载块定义 private bool LoadBlockDefinitions(Database db, Editor ed) { try { using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; // 加载立杆块 string polePath = Path.Combine(BlockDirectory, $"{PoleBlockName}.dwg"); if (!File.Exists(polePath)) { ShowErrorDialog($"立杆块文件未找到: {polePath}"); return false; } if (!bt.Has(PoleBlockName)) { using (Database sourceDb = new Database(false, true)) { sourceDb.ReadDwgFile(polePath, FileOpenMode.OpenForReadAndAllShare, false, null); db.Insert(PoleBlockName, sourceDb, true); ed.WriteMessage($"\n已加载立杆块: {PoleBlockName}"); } } // 加载横杆块 foreach (double length in BarLengths) { string barName = $"{BarPrefix}{length}mm"; string barPath = Path.Combine(BlockDirectory, $"{barName}.dwg"); if (!bt.Has(barName)) { if (File.Exists(barPath)) { using (Database sourceDb = new Database(false, true)) { sourceDb.ReadDwgFile(barPath, FileOpenMode.OpenForReadAndAllShare, false, null); db.Insert(barName, sourceDb, true); } ed.WriteMessage($"\n已加载横杆块: {barName}"); } else { ed.WriteMessage($"\n警告:横杆块文件未找到: {barPath}"); } } } tr.Commit(); } return true; } catch (System.Exception ex) { ShowErrorDialog($"加载块定义失败: {ex.Message}"); return false; } } // 清理现有脚手架 private void CleanOverlappingScaffold(Database db, List<Point3d> points, Editor ed) { if (points.Count == 0) return; // 计算包围盒 double minX = points.Min(p => p.X); double maxX = points.Max(p => p.X); double minY = points.Min(p => p.Y); double maxY = points.Max(p => p.Y); // 扩展边界 double padding = BarLengths.Max() * 1.5; Point3d minPt = new Point3d(minX - padding, minY - padding, 0); Point3d maxPt = new Point3d(maxX + padding, maxY + padding, 0); using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTableRecord btr = tr.GetObject( SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite) as BlockTableRecord; // 创建选择过滤器 TypedValue[] filterValues = { new TypedValue(0, "INSERT"), new TypedValue(8, $"{PoleLayer},{BarLayer}") }; SelectionFilter filter = new SelectionFilter(filterValues); // 创建选择区域 Point3dCollection pts = new Point3dCollection(); pts.Add(minPt); pts.Add(new Point3d(maxPt.X, minPt.Y, 0)); pts.Add(maxPt); pts.Add(new Point3d(minPt.X, maxPt.Y, 0)); // 选择区域内的块 PromptSelectionResult psr = ed.Document.Editor.SelectCrossingPolygon(pts, filter); if (psr.Status == PromptStatus.OK) { int count = 0; foreach (ObjectId id in psr.Value.GetObjectIds()) { Entity ent = tr.GetObject(id, OpenMode.ForWrite) as Entity; if (ent != null) { ent.Erase(); count++; } } ed.WriteMessage($"\n已清理 {count} 个现有脚手架构件"); } tr.Commit(); } } // 布置立杆 private int PlacePoles(Database db, List<Point3d> points) { int count = 0; using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; BlockTableRecord btr = tr.GetObject( SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite) as BlockTableRecord; if (!bt.Has(PoleBlockName)) { Application.ShowAlertDialog($"错误:立杆块 {PoleBlockName} 未加载!"); return 0; } ObjectId poleBlockId = bt[PoleBlockName]; foreach (Point3d pt in points) { if (double.IsNaN(pt.X) || double.IsNaN(pt.Y)) continue; BlockReference br = new BlockReference(pt, poleBlockId); br.Layer = PoleLayer; btr.AppendEntity(br); tr.AddNewlyCreatedDBObject(br, true); count++; } tr.Commit(); } return count; } // 布置横杆 private int PlaceBars(Database db, List<Point3d> validPoints, List<Line> axisLines, Editor ed) { int count = 0; using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; BlockTableRecord btr = tr.GetObject( SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite) as BlockTableRecord; // 创建点空间索引 Point3dTree pointTree = new Point3dTree(validPoints, ToleranceValue * 10); // 处理所有轴网线 foreach (Line line in axisLines) { // 找到线上所有有效交点 List<Point3d> linePoints = pointTree.GetPointsOnLine(line); if (linePoints.Count < 2) { continue; } // 按沿线的距离排序点 linePoints = linePoints.OrderBy(p => p.DistanceTo(line.StartPoint)).ToList(); // 将点分组为连续段 List<List<Point3d>> segments = GroupPointsIntoSegments(linePoints, line, ed); // 在每个连续段上布置横杆 foreach (var segment in segments) { if (segment.Count < 2) continue; for (int i = 0; i < segment.Count - 1; i++) { Point3d start = segment[i]; Point3d end = segment[i + 1]; double distance = start.DistanceTo(end); if (distance < MinDistanceForBar) continue; // 查找最匹配的标准长度 double bestLength = FindBestBarLength(distance); if (bestLength < 0) continue; // 计算中点 Point3d midPoint = new Point3d( (start.X + end.X) / 2, (start.Y + end.Y) / 2, (start.Z + end.Z) / 2); // 计算旋转角度 Vector3d direction = end - start; double angle = direction.GetAngleTo(Vector3d.XAxis, Vector3d.ZAxis); // 处理角度象限问题 if (direction.Y < 0) angle = Math.PI * 2 - angle; // 生成块名 string blockName = $"{BarPrefix}{bestLength}mm"; if (!bt.Has(blockName)) { continue; } // 插入横杆 BlockReference br = new BlockReference(midPoint, bt[blockName]); br.Layer = BarLayer; br.Rotation = angle; btr.AppendEntity(br); tr.AddNewlyCreatedDBObject(br, true); count++; } } } tr.Commit(); } return count; } // 查找最佳横杆长度 private double FindBestBarLength(double distance) { double bestLength = -1; double minDiff = double.MaxValue; foreach (double len in BarLengths) { double diff = Math.Abs(len - distance); if (diff < minDiff && diff <= SpacingTolerance) { minDiff = diff; bestLength = len; } } return bestLength; } // 将点分组为连续段 private List<List<Point3d>> GroupPointsIntoSegments(List<Point3d> points, Line line, Editor ed) { List<List<Point3d>> segments = new List<List<Point3d>>(); if (points.Count < 2) return segments; List<Point3d> currentSegment = new List<Point3d> { points[0] }; double maxGap = BarLengths.Max() * MaxGapFactor; for (int i = 1; i < points.Count; i++) { double distance = points[i - 1].DistanceTo(points[i]); if (distance > maxGap) { if (currentSegment.Count > 1) { segments.Add(currentSegment); } currentSegment = new List<Point3d> { points[i] }; } else { currentSegment.Add(points[i]); } } if (currentSegment.Count > 1) { segments.Add(currentSegment); } return segments; } // 创建图层 private void CreateLayerIfNotExists(Database db, string layerName, short colorIndex) { using (Transaction tr = db.TransactionManager.StartTransaction()) { LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable; if (!lt.Has(layerName)) { lt.UpgradeOpen(); LayerTableRecord ltr = new LayerTableRecord { Name = layerName, Color = Color.FromColorIndex(ColorMethod.ByAci, colorIndex), IsPlottable = true }; lt.Add(ltr); tr.AddNewlyCreatedDBObject(ltr, true); } tr.Commit(); } } // 显示错误对话框 private void ShowErrorDialog(string message) { Application.ShowAlertDialog(message); } } // 空间索引树 public class Point3dTree { private readonly List<Point3d> points; private readonly double tolerance; public Point3dTree(List<Point3d> points, double tolerance) { this.points = points; this.tolerance = tolerance; } public List<Point3d> GetPointsOnLine(Line line) { List<Point3d> result = new List<Point3d>(); // 计算线段包围盒 Point3d min = new Point3d( Math.Min(line.StartPoint.X, line.EndPoint.X) - tolerance, Math.Min(line.StartPoint.Y, line.EndPoint.Y) - tolerance, 0); Point3d max = new Point3d( Math.Max(line.StartPoint.X, line.EndPoint.X) + tolerance, Math.Max(line.StartPoint.Y, line.EndPoint.Y) + tolerance, 0); // 快速筛选候选点 foreach (Point3d pt in points) { if (pt.X >= min.X && pt.X <= max.X && pt.Y >= min.Y && pt.Y <= max.Y) { if (IsPointOnLineSegment(pt, line, tolerance)) { result.Add(pt); } } } return result; } // 检查点是否在线段范围内 private bool IsPointOnLineSegment(Point3d pt, Line line, double tolerance) { Vector3d lineVec = line.EndPoint - line.StartPoint; Vector3d startToPt = pt - line.StartPoint; double lineLengthSquared = lineVec.DotProduct(lineVec); double dotProduct = lineVec.DotProduct(startToPt); return dotProduct >= -tolerance && dotProduct <= lineLengthSquared + tolerance; } }
}
网格斜线段的杆件布置不在斜线上
根据问题重新编写完整代码