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.IO;
using System.Linq;
using SystemException = System.Exception;
// 修复Polyline命名歧义
using DbPolyline = Autodesk.AutoCAD.DatabaseServices.Polyline;
[assembly: CommandClass(typeof(MyCadCommands.CadPlugin))]
namespace MyCadCommands
{
/// <summary>
/// 多段线扩展方法类(存放所有扩展方法,符合非泛型静态类要求)
/// </summary>
public static class PolylineExtensions
{
public static bool ContainsPoint(this DbPolyline pl, Point3d point, double tolerance)
{
for (int i = 0; i < pl.NumberOfVertices; i++)
{
if (pl.GetPoint3dAt(i).DistanceTo(point) < tolerance)
return true;
}
return false;
}
/// <summary>
/// 预缓存多段线的线段信息(起点+终点),避免重复API调用
/// </summary>
public static List<(Point3d Start, Point3d End)> GetSegments(this DbPolyline pl)
{
var segments = new List<(Point3d, Point3d)>();
int vertCount = pl.NumberOfVertices;
for (int i = 0; i < vertCount; i++)
{
int nextIdx = (i + 1) % vertCount;
segments.Add((pl.GetPoint3dAt(i), pl.GetPoint3dAt(nextIdx)));
}
return segments;
}
/// <summary>
/// 【修复】移到静态类中的Vector3d扩展方法:获取向量的垂直向量
/// </summary>
public static Vector3d GetPerpendicularVector(this Vector3d vector)
{
return new Vector3d(-vector.Y, vector.X, vector.Z);
}
}
/// <summary>
/// AutoCAD插件主类(保留DS和GP命令)
/// </summary>
public class CadPlugin
{
// 脚手架绘制常量
private const double DEFAULT_OFFSET_DISTANCE = 250; // 默认离楼层外沿距离(mm)
private const double DEFAULT_SCAFFOLD_WIDTH = 900; // 默认外架宽度(mm)
private const double DEFAULT_MAX_SPACING = 1800; // 默认立杆最大纵距(mm)
private const double OVERLAP_TOLERANCE = 10; // 重叠容差(mm)
private const double RECT_SIDE_LENGTH = 900; // 矩形分隔线边长(mm)
private const double OUTLINE_TOLERANCE = 50; // 轮廓线识别容差(mm)
private Editor _ed; // 日志输出用Editor
private List<Entity> _entitiesToAdd; // 批量添加实体列表(减少数据库交互)
#region DS命令 - 脚手架绘制(优化卡顿版)
[CommandMethod("DS", CommandFlags.Modal)]
public void DrawScaffold()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
_ed = doc.Editor;
_entitiesToAdd = new List<Entity>(); // 初始化批量实体列表
int lineCount = 0, rectCount = 0; // 统计绘制数量(替代频繁日志)
// 保存原始自动保存设置
object oldSaveTime = Application.GetSystemVariable("SAVETIME");
try
{
// 禁用自动保存(避免绘制过程中触发保存)
Application.SetSystemVariable("SAVETIME", 0);
// 1. 获取用户输入参数(无优化,保持原有逻辑)
_ed.WriteMessage("\n=== 脚手架布置参数输入 ===\n");
double offsetDistance = GetInputWithDefault("①请输入离楼层外沿距离(mm)", DEFAULT_OFFSET_DISTANCE);
double scaffoldWidth = GetInputWithDefault("②请输入外架宽(mm)", DEFAULT_SCAFFOLD_WIDTH);
double maxSpacing = GetInputWithDefault("③请输入立杆最大纵距(mm)", DEFAULT_MAX_SPACING);
if (offsetDistance < 0 || scaffoldWidth < 0 || maxSpacing < 0)
{
_ed.WriteMessage("\n❌ 输入参数不能为负数!");
return;
}
// 2. 选择边界线(无优化,保持原有逻辑)
_ed.WriteMessage("\n=== 选择边界线 ===\n");
PromptSelectionOptions pso = new PromptSelectionOptions
{
MessageForAdding = "\n请选择边界线(直线、多段线): ",
RejectObjectsFromNonCurrentSpace = true,
RejectObjectsOnLockedLayers = true
};
TypedValue[] filterList = new TypedValue[]
{
new TypedValue((int)DxfCode.Start, "LINE,POLYLINE,LWPOLYLINE")
};
PromptSelectionResult psr = _ed.GetSelection(pso, new SelectionFilter(filterList));
if (psr.Status == PromptStatus.Cancel)
{
_ed.WriteMessage("\n用户取消操作!");
return;
}
if (psr.Status != PromptStatus.OK || psr.Value.Count == 0)
{
_ed.WriteMessage("\n未选中有效边界线!");
return;
}
// 3. 事务处理(核心优化:批量添加实体)
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
BlockTableRecord modelSpace = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
// 创建必要图层(无优化)
CreateLayerIfNotExists(tr, db, "盘扣轴网");
CreateLayerIfNotExists(tr, db, "标注");
// 检查块引用(无优化)
bool hasBlockRef = psr.Value.Cast<SelectedObject>()
.Any(so => so != null && so.ObjectId.IsValid
&& tr.GetObject(so.ObjectId, OpenMode.ForRead) is BlockReference);
if (hasBlockRef)
{
_ed.WriteMessage("\n检测到选择了块引用,请直接选择边界线而不是块!");
tr.Abort();
return;
}
// 4. 创建闭合轮廓(无优化)
DbPolyline outline = CreateOutlineFromSelectedLines(tr, psr.Value);
if (outline == null)
{
tr.Abort();
return;
}
// 绘制原始轮廓(加入批量列表)
DbPolyline originalOutline = (DbPolyline)outline.Clone();
originalOutline.SetDatabaseDefaults(db);
originalOutline.Layer = "盘扣轴网";
originalOutline.ColorIndex = 5; // 蓝色
originalOutline.Closed = true;
_entitiesToAdd.Add(originalOutline);
// 5. 计算内外轮廓偏移(无优化)
bool isClockwise = IsClockwise(outline);
double outerOffset = offsetDistance + scaffoldWidth;
double innerOffsetVal = isClockwise ? -offsetDistance : offsetDistance;
double outerOffsetVal = isClockwise ? -outerOffset : outerOffset;
DBObjectCollection innerCurves = outline.GetOffsetCurves(innerOffsetVal);
DBObjectCollection outerCurves = outline.GetOffsetCurves(outerOffsetVal);
if (innerCurves.Count == 0 || outerCurves.Count == 0)
{
innerOffsetVal = -innerOffsetVal;
outerOffsetVal = -outerOffsetVal;
innerCurves = outline.GetOffsetCurves(innerOffsetVal);
outerCurves = outline.GetOffsetCurves(outerOffsetVal);
if (innerCurves.Count == 0 || outerCurves.Count == 0)
{
_ed.WriteMessage("\n轮廓偏移失败!");
tr.Abort();
return;
}
}
// 转换偏移曲线(无优化)
DbPolyline innerPl = ConvertToDbPolyline(innerCurves[0], db);
DbPolyline outerPl = ConvertToDbPolyline(outerCurves[0], db);
if (innerPl == null || outerPl == null)
{
_ed.WriteMessage("\n偏移曲线转换失败!");
tr.Abort();
return;
}
// 设置内外轮廓(加入批量列表)
SetPolylineProperties(innerPl, db, "盘扣轴网", 1); // 红色内轮廓
SetPolylineProperties(outerPl, db, "盘扣轴网", 3); // 绿色外轮廓
_entitiesToAdd.Add(innerPl);
_entitiesToAdd.Add(outerPl);
// ---------------------- 优化核心:分隔线绘制 ----------------------
// 预缓存原始轮廓的线段信息(避免重复调用GetPoint3dAt)
var originalSegments = originalOutline.GetSegments();
// 6. 绘制角点分隔线(优化:批量添加+预缓存线段)
if (innerPl.NumberOfVertices >= 3)
{
for (int i = 0; i < innerPl.NumberOfVertices; i++)
{
Point3d innerPt = innerPl.GetPoint3dAt(i);
int nextInnerIdx = (i + 1) % innerPl.NumberOfVertices;
Vector3d tangentDir = (innerPl.GetPoint3dAt(nextInnerIdx) - innerPt).GetNormal();
Vector3d normalDir = GetSegmentNormal(innerPl, i);
// 优化:使用预缓存的线段检测重叠(减少API调用)
bool isOverlap = IsPointOnSegments(innerPt, originalSegments, OVERLAP_TOLERANCE);
if (isOverlap)
{
// 绘制矩形(加入批量列表)
DrawRectangle(innerPt, normalDir, tangentDir, db);
rectCount++;
}
else
{
// 绘制直线(加入批量列表)
DrawLine(innerPt, innerPt + normalDir * scaffoldWidth, db);
lineCount++;
}
}
}
else
{
_ed.WriteMessage("\n内轮廓顶点不足,无法绘制角点分隔线!");
}
// 7. 绘制立杆纵距分隔线(优化:批量添加+预计算参数)
if (innerPl.NumberOfVertices >= 2)
{
for (int i = 0; i < innerPl.NumberOfVertices; i++)
{
int nextIdx = (i + 1) % innerPl.NumberOfVertices;
Point3d start = innerPl.GetPoint3dAt(i);
Point3d end = innerPl.GetPoint3dAt(nextIdx);
double length = start.DistanceTo(end);
// 预计算方向向量(避免循环内重复计算)
Vector3d dir = (end - start).GetNormal();
Vector3d normal = GetSegmentNormal(innerPl, i);
if (length < maxSpacing + 100)
{
continue; // 跳过过短线段(减少日志)
}
double currentOffset = 0;
double remainingLength = length;
// 起点分隔线
bool startOverlap = IsPointOnSegments(start, originalSegments, OVERLAP_TOLERANCE);
if (!startOverlap)
{
DrawLine(start, start + normal * scaffoldWidth, db);
lineCount++;
}
else
{
DrawRectangle(start, normal, dir, db);
rectCount++;
}
// 中间分隔线
while (remainingLength > 0)
{
double nextSpacing = GetNextSpacing(remainingLength, maxSpacing);
if (nextSpacing == 0) break;
currentOffset += nextSpacing;
remainingLength = length - currentOffset;
if (currentOffset > length) break;
Point3d innerPoint = start + dir * currentOffset;
bool midOverlap = IsPointOnSegments(innerPoint, originalSegments, OVERLAP_TOLERANCE);
if (!midOverlap)
{
DrawLine(innerPoint, innerPoint + normal * scaffoldWidth, db);
lineCount++;
}
else
{
DrawRectangle(innerPoint, normal, dir, db);
rectCount++;
}
}
// 终点分隔线
bool endOverlap = IsPointOnSegments(end, originalSegments, OVERLAP_TOLERANCE);
if (!endOverlap)
{
DrawLine(end, end + normal * scaffoldWidth, db);
lineCount++;
}
else
{
DrawRectangle(end, normal, dir, db);
rectCount++;
}
}
}
else
{
_ed.WriteMessage("\n内轮廓线段不足,无法绘制立杆分隔线!");
}
// ---------------------- 批量添加实体(核心优化) ----------------------
foreach (var entity in _entitiesToAdd)
{
if (entity != null)
{
modelSpace.AppendEntity(entity);
tr.AddNewlyCreatedDBObject(entity, true);
}
}
// 提交事务(仅1次提交,减少数据库开销)
tr.Commit();
// 优化:合并日志输出(减少AutoCAD编辑器交互)
_ed.WriteMessage($"\n✅ 脚手架布置成功!");
_ed.WriteMessage($"\n📋 参数:外沿距离{offsetDistance:F0}mm | 外架宽{scaffoldWidth:F0}mm | 立杆纵距{maxSpacing:F0}mm");
_ed.WriteMessage($"\n📌 生成:原始轮廓(蓝) | 内轮廓(红) | 外轮廓(绿) | 直线分隔线({lineCount}条) | 矩形分隔线({rectCount}个)");
}
}
catch (System.Exception ex)
{
_ed.WriteMessage($"\n❌ 脚手架绘制错误:{ex.Message}\n堆栈:{ex.StackTrace}");
}
finally
{
// 恢复自动保存设置
if (oldSaveTime != null)
{
Application.SetSystemVariable("SAVETIME", oldSaveTime);
}
_entitiesToAdd?.Clear(); // 释放资源
}
}
#endregion
#region GP命令 - 钢踏板布置(修改版:不再依赖DS命令,用户手动选择轮廓线)
[CommandMethod("GP", CommandFlags.Modal)]
public void ArrangeSteelPedal()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
_ed = doc.Editor;
try
{
_ed.WriteMessage("\n=== 钢踏板布置 ===\n");
// 1. 提示用户选择内外轮廓线
_ed.WriteMessage("\n请选择内轮廓线(第一道轮廓线): ");
PromptEntityOptions peoInner = new PromptEntityOptions("\n请选择内轮廓线(第一道轮廓线): ");
peoInner.SetRejectMessage("\n请选择多段线!");
peoInner.AddAllowedClass(typeof(DbPolyline), true);
PromptEntityResult perInner = _ed.GetEntity(peoInner);
if (perInner.Status == PromptStatus.Cancel)
{
_ed.WriteMessage("\n用户取消操作!");
return;
}
_ed.WriteMessage("\n请选择外轮廓线(第二道轮廓线): ");
PromptEntityOptions peoOuter = new PromptEntityOptions("\n请选择外轮廓线(第二道轮廓线): ");
peoOuter.SetRejectMessage("\n请选择多段线!");
peoOuter.AddAllowedClass(typeof(DbPolyline), true);
PromptEntityResult perOuter = _ed.GetEntity(peoOuter);
if (perOuter.Status == PromptStatus.Cancel)
{
_ed.WriteMessage("\n用户取消操作!");
return;
}
// 2. 选择所有横杆图块
_ed.WriteMessage("\n请框选所有横杆图块: ");
PromptSelectionOptions pso = new PromptSelectionOptions
{
MessageForAdding = "\n选择所有横杆图块: ",
RejectObjectsFromNonCurrentSpace = true,
RejectObjectsOnLockedLayers = true
};
TypedValue[] filterList = new TypedValue[]
{
new TypedValue((int)DxfCode.Start, "INSERT")
};
PromptSelectionResult psr = _ed.GetSelection(pso, new SelectionFilter(filterList));
if (psr.Status == PromptStatus.Cancel)
{
_ed.WriteMessage("\n用户取消操作!");
return;
}
if (psr.Status != PromptStatus.OK || psr.Value.Count == 0)
{
_ed.WriteMessage("\n未选中有效横杆图块!");
return;
}
// 3. 获取插件目录下的ScaffoldBlocks文件夹路径
string pluginPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
string scaffoldBlocksPath = Path.Combine(pluginPath, "ScaffoldBlocks");
if (!Directory.Exists(scaffoldBlocksPath))
{
_ed.WriteMessage($"\n❌ 找不到ScaffoldBlocks文件夹: {scaffoldBlocksPath}");
return;
}
// 4. 检查所需的钢踏板图块文件是否存在
Dictionary<string, string> pedalBlockPaths = new Dictionary<string, string>
{
{"900", Path.Combine(scaffoldBlocksPath, "ScaffoldPole钢踏板900mm.dwg")},
{"1200", Path.Combine(scaffoldBlocksPath, "ScaffoldPole钢踏板1200mm.dwg")},
{"1500", Path.Combine(scaffoldBlocksPath, "ScaffoldPole钢踏板1500mm.dwg")},
{"1800", Path.Combine(scaffoldBlocksPath, "ScaffoldPole钢踏板1800mm.dwg")},
{"single", Path.Combine(scaffoldBlocksPath, "ScaffoldPole单片钢踏板.dwg")}
};
foreach (var blockPath in pedalBlockPaths)
{
if (!File.Exists(blockPath.Value))
{
_ed.WriteMessage($"\n❌ 找不到钢踏板图块文件: {blockPath.Value}");
return;
}
}
_ed.WriteMessage($"\n✅ 所有钢踏板图块文件都存在");
// 5. 处理选中的横杆图块
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
BlockTableRecord modelSpace = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
// 获取内外轮廓线
DbPolyline innerPl = tr.GetObject(perInner.ObjectId, OpenMode.ForRead) as DbPolyline;
DbPolyline outerPl = tr.GetObject(perOuter.ObjectId, OpenMode.ForRead) as DbPolyline;
if (innerPl == null || outerPl == null)
{
_ed.WriteMessage("\n❌ 选择的轮廓线无效!");
return;
}
int processedCount = 0;
int pedalCount = 0;
int skippedCount = 0;
foreach (SelectedObject so in psr.Value)
{
if (so == null || !so.ObjectId.IsValid) continue;
BlockReference blockRef = tr.GetObject(so.ObjectId, OpenMode.ForRead) as BlockReference;
if (blockRef == null) continue;
// 获取图块名称
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(blockRef.BlockTableRecord, OpenMode.ForRead);
string blockName = btr.Name;
// 解析横杆长度
double beamLength = ParseBeamLength(blockName);
if (beamLength <= 0) continue;
// 检查横杆是否靠近第二道轮廓线(外轮廓)
Point3d blockPosition = blockRef.Position;
double distanceToInner = GetDistanceToPolyline(blockPosition, innerPl);
double distanceToOuter = GetDistanceToPolyline(blockPosition, outerPl);
// 如果横杆不靠近外轮廓,则跳过(忽略第一道轮廓线和分隔线上的横杆)
if (distanceToOuter > OUTLINE_TOLERANCE)
{
skippedCount++;
continue;
}
// 确定对应的钢踏板图块
string pedalBlockName = GetPedalBlockName(beamLength);
if (string.IsNullOrEmpty(pedalBlockName)) continue;
// 导入钢踏板图块定义(如果尚未导入)
if (!bt.Has(pedalBlockName))
{
using (Database sourceDb = new Database(false, true))
{
try
{
sourceDb.ReadDwgFile(pedalBlockPaths[pedalBlockName], FileShare.Read, true, "");
ObjectId mappingId = db.Insert(pedalBlockName, sourceDb, true);
if (!mappingId.IsValid)
{
_ed.WriteMessage($"\n❌ 导入钢踏板图块失败: {pedalBlockName}");
continue;
}
}
catch (SystemException ex)
{
_ed.WriteMessage($"\n❌ 读取图块文件失败: {pedalBlockPaths[pedalBlockName]}, 错误: {ex.Message}");
continue;
}
}
}
// 计算钢踏板布置数量和位置
int pedalQuantity = CalculatePedalQuantity(beamLength, pedalBlockName);
if (pedalQuantity <= 0) continue;
// 布置钢踏板
if (PlacePedals(tr, modelSpace, blockRef, pedalBlockName, pedalQuantity, beamLength))
{
processedCount++;
pedalCount += pedalQuantity;
}
}
tr.Commit();
_ed.WriteMessage($"\n✅ 钢踏板布置完成!处理了 {processedCount} 根第二道轮廓线上的横杆,共布置 {pedalCount} 块钢踏板");
if (skippedCount > 0)
{
_ed.WriteMessage($"\n⚠️ 跳过了 {skippedCount} 根第一道轮廓线及分隔线上的横杆");
}
}
}
catch (System.Exception ex)
{
_ed.WriteMessage($"\n❌ 钢踏板布置错误:{ex.Message}\n堆栈:{ex.StackTrace}");
}
}
/// <summary>
/// 计算点到多段线的最短距离
/// </summary>
private double GetDistanceToPolyline(Point3d point, DbPolyline pl)
{
double minDistance = double.MaxValue;
for (int i = 0; i < pl.NumberOfVertices; i++)
{
int nextIdx = (i + 1) % pl.NumberOfVertices;
Point3d start = pl.GetPoint3dAt(i);
Point3d end = pl.GetPoint3dAt(nextIdx);
double distance = PointToLineDistance(point, start, end);
if (distance < minDistance)
{
minDistance = distance;
}
}
return minDistance;
}
/// <summary>
/// 从横杆图块名称中解析长度
/// </summary>
private double ParseBeamLength(string blockName)
{
if (blockName.Contains("横杆"))
{
if (blockName.Contains("1800mm")) return 1800;
if (blockName.Contains("1500mm")) return 1500;
if (blockName.Contains("1200mm")) return 1200;
if (blockName.Contains("900mm")) return 900;
if (blockName.Contains("600mm")) return 600;
if (blockName.Contains("300mm")) return 300;
}
return 0;
}
/// <summary>
/// 根据横杆长度确定钢踏板图块名称
/// </summary>
private string GetPedalBlockName(double beamLength)
{
switch (beamLength)
{
case 1800: return "1800";
case 1500: return "1500";
case 1200: return "1200";
case 900: return "900";
case 600:
case 300:
default: return "single";
}
}
/// <summary>
/// 计算钢踏板布置数量
/// </summary>
private int CalculatePedalQuantity(double beamLength, string pedalBlockName)
{
if (pedalBlockName == "single")
{
// 单片钢踏板宽度为240mm
return (int)Math.Ceiling(beamLength / 240.0);
}
return 1; // 标准长度只需一块钢踏板
}
/// <summary>
/// 布置钢踏板(调用GetPerpendicularVector扩展方法,语法不变)
/// </summary>
private bool PlacePedals(Transaction tr, BlockTableRecord modelSpace, BlockReference beamRef,
string pedalBlockName, int quantity, double beamLength)
{
try
{
BlockTable bt = (BlockTable)tr.GetObject(beamRef.Database.BlockTableId, OpenMode.ForRead);
// 获取钢踏板图块定义
if (!bt.Has(pedalBlockName)) return false;
ObjectId pedalBlockId = bt[pedalBlockName];
// 获取横杆的插入点和旋转角度
Point3d insertionPoint = beamRef.Position;
double rotation = beamRef.Rotation;
// 计算钢踏板布置方向和间距(此处调用扩展方法,因已移到静态类,可正常使用)
Vector3d direction = new Vector3d(Math.Cos(rotation), Math.Sin(rotation), 0);
Vector3d perpendicular = direction.GetPerpendicularVector();
// 计算单片钢踏板宽度
double singlePedalWidth = 240.0;
if (pedalBlockName != "single")
{
// 标准钢踏板直接使用横杆长度
singlePedalWidth = beamLength;
}
// 计算总宽度和起始位置
double totalWidth = quantity * singlePedalWidth;
// 修改起始位置:从负的钢踏板长度开始布置
double startOffset = -singlePedalWidth; // 从负的钢踏板长度开始
for (int i = 0; i < quantity; i++)
{
// 计算当前钢踏板的位置
double offset = startOffset + i * singlePedalWidth + singlePedalWidth / 2.0;
Point3d pedalPosition = insertionPoint + direction * offset;
// 创建钢踏板图块引用
BlockReference pedalRef = new BlockReference(pedalPosition, pedalBlockId);
pedalRef.Rotation = rotation;
// 设置钢踏板图层和属性
pedalRef.Layer = "盘扣轴网";
pedalRef.ColorIndex = 6; // 洋红色
// 添加到模型空间
modelSpace.AppendEntity(pedalRef);
tr.AddNewlyCreatedDBObject(pedalRef, true);
}
return true;
}
catch (SystemException ex)
{
_ed.WriteMessage($"\n❌ 布置钢踏板失败: {ex.Message}");
return false;
}
}
#endregion
#region 优化后的辅助方法
/// <summary>
/// 获取带默认值的用户输入(无优化)
/// </summary>
private double GetInputWithDefault(string prompt, double defaultValue)
{
PromptDoubleOptions pdo = new PromptDoubleOptions($"\n{prompt} [默认: {defaultValue}]: ");
pdo.DefaultValue = defaultValue;
pdo.AllowNone = true;
PromptDoubleResult pdr = _ed.GetDouble(pdo);
if (pdr.Status == PromptStatus.Cancel) return -1;
if (pdr.Status == PromptStatus.None) return defaultValue;
return pdr.Value;
}
/// <summary>
/// 预计算下一个间距(提取重复逻辑,减少循环内代码量)
/// </summary>
private double GetNextSpacing(double remainingLength, double maxSpacing)
{
if (remainingLength >= maxSpacing) return maxSpacing;
if (remainingLength >= 1500) return 1500;
if (remainingLength >= 1200) return 1200;
if (remainingLength >= 900) return 900;
if (remainingLength >= 600) return 600;
if (remainingLength >= 300) return 300;
return 0; // 小于300mm跳过
}
/// <summary>
/// 使用预缓存的线段检测点重叠(减少AutoCAD API调用)
/// </summary>
private bool IsPointOnSegments(Point3d point, List<(Point3d Start, Point3d End)> segments, double tolerance)
{
foreach (var (start, end) in segments)
{
double dist = PointToLineDistance(point, start, end);
if (dist < tolerance)
return true; // 找到重叠立即返回,减少循环
}
return false;
}
/// <summary>
/// 绘制直线(仅创建对象,不立即添加到数据库)
/// </summary>
private void DrawLine(Point3d start, Point3d end, Database db)
{
Line line = new Line(start, end);
line.SetDatabaseDefaults(db);
line.Layer = "盘扣轴网";
line.ColorIndex = 4; // 青色
_entitiesToAdd.Add(line);
}
/// <summary>
/// 绘制矩形(仅创建对象,不立即添加到数据库)
/// </summary>
private void DrawRectangle(Point3d startPoint, Vector3d normalDir, Vector3d tangentDir, Database db)
{
Vector3d normal = normalDir.GetNormal();
Vector3d tangent = tangentDir.GetNormal();
// 计算矩形顶点(无逻辑变化)
Point3d p0 = startPoint;
Point3d p1 = p0 + normal * RECT_SIDE_LENGTH;
Point3d p3 = p0 + tangent * RECT_SIDE_LENGTH;
Point3d p2 = p1 + tangent * RECT_SIDE_LENGTH;
DbPolyline rectPoly = new DbPolyline();
rectPoly.SetDatabaseDefaults(db);
rectPoly.Layer = "盘扣轴网";
rectPoly.ColorIndex = 4; // 青色
rectPoly.Closed = true;
rectPoly.AddVertexAt(0, new Point2d(p0.X, p0.Y), 0, 0, 0);
rectPoly.AddVertexAt(1, new Point2d(p1.X, p1.Y), 0, 0, 0);
rectPoly.AddVertexAt(2, new Point2d(p2.X, p2.Y), 0, 0, 0);
rectPoly.AddVertexAt(3, new Point2d(p3.X, p3.Y), 0, 0, 0);
_entitiesToAdd.Add(rectPoly);
}
/// <summary>
/// 设置多段线属性(移除事务参数,仅处理对象本身)
/// </summary>
private void SetPolylineProperties(DbPolyline pl, Database db, string layer, int colorIndex)
{
pl.SetDatabaseDefaults(db);
pl.Closed = true;
pl.Layer = layer;
pl.ColorIndex = colorIndex;
}
// 以下辅助方法无核心逻辑变化,仅适配优化后的参数传递
private DbPolyline CreateOutlineFromSelectedLines(Transaction tr, SelectionSet ss)
{
try
{
List<Line> selectedLines = new List<Line>();
List<DbPolyline> selectedPolylines = new List<DbPolyline>();
foreach (SelectedObject so in ss)
{
if (so == null || !so.ObjectId.IsValid) continue;
Entity ent = tr.GetObject(so.ObjectId, OpenMode.ForRead) as Entity;
if (ent is Line line) selectedLines.Add(line);
else if (ent is DbPolyline pl) selectedPolylines.Add(pl);
}
if (selectedPolylines.Count > 0)
{
if (selectedPolylines.Count == 1 && selectedPolylines[0].Closed)
return selectedPolylines[0].Clone() as DbPolyline;
return JoinPolylines(selectedPolylines);
}
if (selectedLines.Count < 2)
{
_ed.WriteMessage("\n至少需要选择两条线段才能形成闭合轮廓!");
return null;
}
List<Point3d> intersectionPoints = new List<Point3d>();
for (int i = 0; i < selectedLines.Count; i++)
{
for (int j = i + 1; j < selectedLines.Count; j++)
{
Point3dCollection intersectPoints = new Point3dCollection();
selectedLines[i].IntersectWith(selectedLines[j], Intersect.OnBothOperands, intersectPoints, IntPtr.Zero, IntPtr.Zero);
foreach (Point3d pt in intersectPoints) intersectionPoints.Add(pt);
}
}
if (intersectionPoints.Count < 2)
{
_ed.WriteMessage("\n选择的线段没有足够的交点形成闭合轮廓!");
return null;
}
return CreatePolylineFromIntersections(selectedLines, intersectionPoints);
}
catch (System.Exception ex)
{
_ed.WriteMessage($"\n轮廓生成错误:{ex.Message}");
return null;
}
}
private DbPolyline CreatePolylineFromIntersections(List<Line> lines, List<Point3d> intersections)
{
try
{
List<Point3d> boundaryPoints = new List<Point3d>(intersections);
Point3dComparer comparer = new Point3dComparer(0.001);
foreach (Line line in lines)
{
if (!boundaryPoints.Contains(line.StartPoint, comparer)) boundaryPoints.Add(line.StartPoint);
if (!boundaryPoints.Contains(line.EndPoint, comparer)) boundaryPoints.Add(line.EndPoint);
}
List<Point3d> convexHull = ComputeConvexHull(boundaryPoints);
DbPolyline polyline = new DbPolyline();
for (int i = 0; i < convexHull.Count; i++)
{
polyline.AddVertexAt(i, new Point2d(convexHull[i].X, convexHull[i].Y), 0, 0, 0);
}
polyline.Closed = true;
return polyline;
}
catch (System.Exception ex)
{
_ed.WriteMessage($"\n创建轮廓错误:{ex.Message}");
return null;
}
}
private List<Point3d> ComputeConvexHull(List<Point3d> points)
{
if (points.Count < 3) return points;
Point3d startPoint = points[0];
foreach (Point3d p in points)
{
if (p.X < startPoint.X || (Math.Abs(p.X - startPoint.X) < 0.001 && p.Y < startPoint.Y))
{
startPoint = p;
}
}
List<Point3d> sortedPoints = new List<Point3d>(points);
sortedPoints.Sort((a, b) =>
{
if (a == startPoint) return -1;
if (b == startPoint) return 1;
double cross = CrossProduct(startPoint, a, b);
if (Math.Abs(cross) < 0.001)
return startPoint.DistanceTo(a).CompareTo(startPoint.DistanceTo(b));
return cross > 0 ? -1 : 1;
});
List<Point3d> hull = new List<Point3d> { startPoint };
if (sortedPoints.Count > 1) hull.Add(sortedPoints[1]);
for (int i = 2; i < sortedPoints.Count; i++)
{
while (hull.Count >= 2 && CrossProduct(hull[hull.Count - 2], hull[hull.Count - 1], sortedPoints[i]) <= 0)
{
hull.RemoveAt(hull.Count - 1);
}
hull.Add(sortedPoints[i]);
}
return hull;
}
private double CrossProduct(Point3d o, Point3d a, Point3d b)
{
return (a.X - o.X) * (b.Y - o.Y) - (a.Y - o.Y) * (b.X - o.X);
}
private DbPolyline JoinPolylines(List<DbPolyline> polylines)
{
return polylines[0].Clone() as DbPolyline;
}
private DbPolyline ConvertToDbPolyline(DBObject obj, Database db)
{
if (obj is DbPolyline pl)
{
pl.SetDatabaseDefaults(db);
return pl;
}
if (obj is Line line)
{
DbPolyline newPl = new DbPolyline();
newPl.SetDatabaseDefaults(db);
newPl.AddVertexAt(0, new Point2d(line.StartPoint.X, line.StartPoint.Y), 0, 0, 0);
newPl.AddVertexAt(1, new Point2d(line.EndPoint.X, line.EndPoint.Y), 0, 0, 0);
newPl.Closed = false;
return newPl;
}
if (obj is Arc arc)
{
DbPolyline newPl = new DbPolyline();
newPl.SetDatabaseDefaults(db);
double startAng = arc.StartAngle;
double endAng = arc.EndAngle;
double centralAng = endAng - startAng;
if (centralAng < 0) centralAng += 2 * Math.PI;
if (IsArcClockWise(arc)) centralAng = -centralAng;
double bulge = Math.Tan(centralAng / 4);
newPl.AddVertexAt(0, new Point2d(arc.StartPoint.X, arc.StartPoint.Y), bulge, 0, 0);
newPl.AddVertexAt(1, new Point2d(arc.EndPoint.X, arc.EndPoint.Y), 0, 0, 0);
newPl.Closed = false;
return newPl;
}
return null;
}
private bool IsArcClockWise(Arc arc)
{
Point3d center = arc.Center;
Point3d start = arc.StartPoint;
Point3d end = arc.EndPoint;
Vector2d vec1 = new Vector2d(start.X - center.X, start.Y - center.Y);
Vector2d vec2 = new Vector2d(end.X - center.X, end.Y - center.Y);
double crossProduct = vec1.X * vec2.Y - vec1.Y * vec2.X;
return crossProduct < 0;
}
private bool IsClockwise(DbPolyline pl)
{
if (pl == null || pl.NumberOfVertices < 3) return false;
double area = 0;
int vertCount = pl.NumberOfVertices;
for (int i = 0; i < vertCount; i++)
{
Point2d p1 = pl.GetPoint2dAt(i);
Point2d p2 = pl.GetPoint2dAt((i + 1) % vertCount);
area += p1.X * p2.Y - p2.X * p1.Y;
}
return area < 0;
}
private Vector3d GetSegmentNormal(DbPolyline pl, int segmentIndex)
{
if (pl == null || segmentIndex < 0 || segmentIndex >= pl.NumberOfVertices)
return Vector3d.ZAxis;
int nextIdx = (segmentIndex + 1) % pl.NumberOfVertices;
Point3d start = pl.GetPoint3dAt(segmentIndex);
Point3d end = pl.GetPoint3dAt(nextIdx);
Vector3d dir = (end - start).GetNormal();
Vector3d normal = new Vector3d(-dir.Y, dir.X, 0).GetNormal();
Point3d midPoint = start + (end - start) * 0.5;
Point3d testPoint = midPoint + normal * 10;
if (IsPointInsidePolyline(pl, testPoint))
normal = -normal;
return normal;
}
private bool IsPointInsidePolyline(DbPolyline pl, Point3d point)
{
if (pl == null || !pl.Closed) return false;
int crossings = 0;
int vertexCount = pl.NumberOfVertices;
for (int i = 0; i < vertexCount; i++)
{
int j = (i + 1) % vertexCount;
Point3d vi = pl.GetPoint3dAt(i);
Point3d vj = pl.GetPoint3dAt(j);
if (((vi.Y <= point.Y) && (vj.Y > point.Y)) || ((vj.Y <= point.Y) && (vi.Y > point.Y)))
{
double vt = (point.Y - vi.Y) / (vj.Y - vi.Y);
if (point.X < vi.X + vt * (vj.X - vi.X))
crossings++;
}
}
return (crossings % 2) != 0;
}
private double PointToLineDistance(Point3d point, Point3d lineStart, Point3d lineEnd)
{
Vector3d lineVec = lineEnd - lineStart;
Vector3d pointVec = point - lineStart;
double lineLength = lineVec.Length;
if (lineLength < 1e-10) return pointVec.Length;
Vector3d normalizedLineVec = lineVec / lineLength;
double projLength = pointVec.X * normalizedLineVec.X + pointVec.Y * normalizedLineVec.Y + pointVec.Z * normalizedLineVec.Z;
if (projLength < 0) return pointVec.Length;
if (projLength > lineLength) return (point - lineEnd).Length;
Point3d projPoint = lineStart + normalizedLineVec * projLength;
return point.DistanceTo(projPoint);
}
private void CreateLayerIfNotExists(Transaction tr, Database db, string layerName)
{
if (string.IsNullOrEmpty(layerName)) return;
LayerTable lt = (LayerTable)tr.GetObject(db.LayerTableId, OpenMode.ForRead);
if (!lt.Has(layerName))
{
lt.UpgradeOpen();
LayerTableRecord ltr = new LayerTableRecord { Name = layerName };
lt.Add(ltr);
tr.AddNewlyCreatedDBObject(ltr, true);
lt.DowngradeOpen();
_ed.WriteMessage($"\n创建图层:{layerName}");
}
}
#endregion
}
/// <summary>
/// Point3d比较器(含容差)
/// </summary>
public class Point3dComparer : IEqualityComparer<Point3d>
{
private double _tolerance;
public Point3dComparer(double tolerance)
{
_tolerance = tolerance;
}
public bool Equals(Point3d x, Point3d y)
{
return x.DistanceTo(y) < _tolerance;
}
public int GetHashCode(Point3d obj)
{
return obj.X.GetHashCode() ^ obj.Y.GetHashCode() ^ obj.Z.GetHashCode();
}
}
}
第二道轮廓线转角处X方向900mm横杆和Y方向900mm横杆,只布置X方向900mm横杆的900mm钢踏板Y方向900mm横杆处不布置钢踏板