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.Reflection;
using System.Windows.Forms;
using Application = Autodesk.AutoCAD.ApplicationServices.Application;
namespace ScaffoldSteelPlugin
{
public class ScaffoldCommands
{
// 共享记忆数据(步骤间传递)
private static double _memorizedDistance; // 步骤1量取的尺寸
private static List<BeamInfo> _memorizedBeams = new List<BeamInfo>(); // 步骤2记忆的横杆信息
private static Vector3d _memorizedDirection; // 步骤2识别的横杆方向
private static SteelSettings _currentSettings; // 当前方钢设置参数
private static readonly List<string> _beamBlockNames = new List<string>
{
"ScaffoldPole横杆300mm",
"ScaffoldPole横杆600mm",
"ScaffoldPole横杆900mm",
"ScaffoldPole横杆1200mm",
"ScaffoldPole横杆1500mm",
"ScaffoldPole横杆1800mm"
}; // 支持的横杆图块名列表
private static readonly List<double> _steelStandardLengths = new List<double>
{
1000,1500,2000,2500,3000,3500,4000,4500,6000
}; // 方钢标准长度(mm)
// 横杆信息类
private class BeamInfo
{
public Point3d Position { get; set; }
public double Rotation { get; set; }
public Vector3d Direction { get; set; }
public double Length { get; set; }
public string BlockName { get; set; }
}
// 方钢设置参数类
public class SteelSettings
{
public bool IsUnderSlab { get; set; } // 是否为板下方钢
public bool IsUnderBeam { get; set; } // 是否为梁下方钢
public double Height { get; set; } // 方钢高度
public double Width { get; set; } // 方钢宽度
public string Direction { get; set; } // 布置方向
public string PlacementType { get; set; } // 使用部位
}
// 统计信息类
private class SteelStatisticsInfo
{
public string MaterialName { get; set; } // 材料名称
public string Specification { get; set; } // 规格
public string PlacementType { get; set; } // 使用部位
public double Length { get; set; } // 长度
public int Count { get; set; } // 数量
}
/// <summary>
/// 显示参数设置窗口
/// </summary>
private static bool ShowSettingsDialog()
{
try
{
// 创建并显示参数设置窗体
SettingsForm form = new SettingsForm();
DialogResult result = Application.ShowModalDialog(form);
if (result == DialogResult.OK)
{
_currentSettings = form.GetSettings();
return true;
}
return false;
}
catch (System.Exception ex)
{
Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage($"\n显示设置窗口错误:{ex.Message}");
return false;
}
}
/// <summary>
/// 主命令:脚手架方钢布置(缩写SP)
/// </summary>
[CommandMethod("ScaffoldPlace", "SP", CommandFlags.Modal)]
public void ScaffoldSteelPlacement()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
Database db = doc.Database;
try
{
// 显示参数设置窗口
if (!ShowSettingsDialog()) return;
// 步骤1:量取尺寸并记忆
if (!Step1_MeasureAndMemorize(ed)) return;
// 步骤2:框选横杆、识别方向
if (!Step2_SelectBeamAndMemorize(ed, db)) return;
// 步骤3:输入参数并布置方钢
Step3_InputParamsAndPlaceSteel(ed, db);
}
catch (System.Exception ex)
{
ed.WriteMessage($"\n插件执行错误:{ex.Message}");
}
}
/// <summary>
/// 方钢统计命令(缩写SS)
/// </summary>
[CommandMethod("ScaffoldStat", "SS", CommandFlags.Modal)]
public void ScaffoldSteelStatistics()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
Database db = doc.Database;
try
{
ed.WriteMessage("\n=== 方钢统计 ===");
ed.WriteMessage("\n请框选要统计的图块:");
// 创建过滤器,选择方钢和双槽钢拖梁图块
TypedValue[] filterList = new TypedValue[] {
new TypedValue((int)DxfCode.Start, "INSERT"),
new TypedValue((int)DxfCode.BlockName, "*方钢*,*双槽钢拖梁*")
};
SelectionFilter filter = new SelectionFilter(filterList);
PromptSelectionResult selRes = ed.GetSelection(filter);
if (selRes.Status != PromptStatus.OK)
{
ed.WriteMessage("\n取消选择操作");
return;
}
if (selRes.Value.Count == 0)
{
ed.WriteMessage("\n未选中任何图块");
return;
}
// 统计数据
Dictionary<string, SteelStatisticsInfo> steelStatistics = new Dictionary<string, SteelStatisticsInfo>();
using (Transaction tr = db.TransactionManager.StartTransaction())
{
foreach (SelectedObject selObj in selRes.Value)
{
Entity ent = tr.GetObject(selObj.ObjectId, OpenMode.ForRead) as Entity;
if (ent is BlockReference br)
{
if (br.Name.Contains("方钢"))
{
ProcessSteelStatistics(br, steelStatistics);
}
else if (br.Name.Contains("双槽钢拖梁"))
{
ProcessChannelSteelStatistics(br, steelStatistics);
}
}
}
tr.Commit();
}
if (steelStatistics.Count == 0)
{
ed.WriteMessage("\n未找到有效的图块");
return;
}
// 获取插入点
PromptPointResult ptRes = ed.GetPoint("\n指定表格插入点: ");
if (ptRes.Status != PromptStatus.OK)
{
ed.WriteMessage("\n取消表格插入");
return;
}
// 创建统计表格
CreateStatisticsTable(db, ptRes.Value, steelStatistics);
ed.WriteMessage($"\n统计完成!共统计{steelStatistics.Count}种规格的材料");
}
catch (System.Exception ex)
{
ed.WriteMessage($"\n统计过程错误:{ex.Message}");
}
}
// 处理方钢统计
private void ProcessSteelStatistics(BlockReference br, Dictionary<string, SteelStatisticsInfo> steelStatistics)
{
// 从图层名中提取使用部位和长度信息
string layerName = br.Layer;
string placementType = "板下方钢"; // 默认值
double lengthInMm = 0;
// 从图层名解析使用部位和长度
if (layerName.Contains("板下方钢"))
{
placementType = "板下方钢";
string lengthStr = layerName.Replace("板下方钢", "").Replace("m", "");
if (double.TryParse(lengthStr, out double lengthInMeters))
{
lengthInMm = lengthInMeters * 1000;
}
}
else if (layerName.Contains("梁下方钢"))
{
placementType = "梁下方钢";
string lengthStr = layerName.Replace("梁下方钢", "").Replace("m", "");
if (double.TryParse(lengthStr, out double lengthInMeters))
{
lengthInMm = lengthInMeters * 1000;
}
}
// 从扩展数据中获取规格信息
double height = 100; // 默认值
double width = 50; // 默认值
// 读取扩展数据
if (br.XData != null)
{
ResultBuffer rb = br.XData;
TypedValue[] values = rb.AsArray();
for (int i = 0; i < values.Length; i++)
{
if (values[i].TypeCode == 1040) // 实数类型
{
if (height == 100) // 第一个实数是高度
{
height = (double)values[i].Value;
}
else if (width == 50) // 第二个实数是宽度
{
width = (double)values[i].Value;
}
}
}
}
// 如果无法从图层名获取长度,则从块名中提取
if (lengthInMm == 0)
{
string lengthStr = br.Name.Replace("m方钢", "");
if (double.TryParse(lengthStr, out double lengthInMeters))
{
lengthInMm = lengthInMeters * 1000;
}
}
// 创建统计键 - 包含使用部位信息
string statKey = $"方钢_{height}x{width}_{placementType}_{lengthInMm}";
if (steelStatistics.ContainsKey(statKey))
{
steelStatistics[statKey].Count++;
}
else
{
steelStatistics[statKey] = new SteelStatisticsInfo
{
MaterialName = "方钢",
Specification = $"{height}x{width}",
PlacementType = placementType,
Length = lengthInMm,
Count = 1
};
}
}
// 处理双槽钢拖梁统计
private void ProcessChannelSteelStatistics(BlockReference br, Dictionary<string, SteelStatisticsInfo> steelStatistics)
{
// 从块名中提取长度
double lengthInMm = 0;
if (br.Name.Contains("1350mm"))
{
lengthInMm = 1350;
}
else if (br.Name.Contains("1650mm"))
{
lengthInMm = 1650;
}
// 创建统计键
string statKey = $"双槽钢拖梁_10#{lengthInMm}";
if (steelStatistics.ContainsKey(statKey))
{
steelStatistics[statKey].Count++;
}
else
{
steelStatistics[statKey] = new SteelStatisticsInfo
{
MaterialName = "双槽钢拖梁",
Specification = "10#",
PlacementType = "",
Length = lengthInMm,
Count = 1
};
}
}
// 创建统计表格方法
private void CreateStatisticsTable(Database db, Point3d insertionPoint, Dictionary<string, SteelStatisticsInfo> steelStatistics)
{
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
// 计算行数:标题行 + 表头行 + 数据行 + 说明行
int rowCount = 1 + 1 + steelStatistics.Count + 1; // 标题行+表头行+数据行+说明行
int colCount = 6; // 序号、材料名称、规格、使用部位、长度(mm)、数量统计
Table table = new Table();
table.SetSize(rowCount, colCount);
table.Position = insertionPoint;
// 设置表格样式
table.SetRowHeight(0, 12); // 标题行高度
for (int i = 1; i < rowCount; i++)
{
table.SetRowHeight(i, 8); // 其他行高度
}
// 设置列宽
table.SetColumnWidth(0, 15); // 序号列
table.SetColumnWidth(1, 30); // 材料名称列
table.SetColumnWidth(2, 25); // 规格列
table.SetColumnWidth(3, 25); // 使用部位列
table.SetColumnWidth(4, 25); // 长度列
table.SetColumnWidth(5, 20); // 数量统计列
// 设置标题行 - 合并所有列
table.MergeCells(CellRange.Create(table, 0, 0, 0, colCount - 1));
table.SetTextHeight(0, 0, 5);
table.SetAlignment(0, 0, CellAlignment.MiddleCenter);
table.SetTextString(0, 0, "材料统计表");
// 设置表头行
string[] headers = { "序号", "材料名称", "规格", "使用部位", "长度(mm)", "数量统计" };
for (int col = 0; col < colCount; col++)
{
table.SetTextHeight(1, col, 3.5);
table.SetAlignment(1, col, CellAlignment.MiddleCenter);
table.SetTextString(1, col, headers[col]);
}
// 填充数据行
int rowIndex = 2;
int serialNumber = 1;
// 直接统计所有材料,不添加分类标题
foreach (var item in steelStatistics.Values)
{
FillTableRow(table, rowIndex, serialNumber, item);
serialNumber++;
rowIndex++;
}
// 添加说明行 - 合并所有列
table.MergeCells(CellRange.Create(table, rowIndex, 0, rowIndex, colCount - 1));
table.SetTextHeight(rowIndex, 0, 3.0); // 减小说明文字大小
table.SetAlignment(rowIndex, 0, CellAlignment.MiddleLeft);
table.SetTextString(rowIndex, 0, "说明:本表统计的材料用量属预估量,具体实际用量按施工用量为准。");
// 将表格添加到模型空间
btr.AppendEntity(table);
tr.AddNewlyCreatedDBObject(table, true);
tr.Commit();
}
}
// 辅助方法:填充表格行
private void FillTableRow(Table table, int rowIndex, int serialNumber, SteelStatisticsInfo info)
{
// 序号列
table.SetTextHeight(rowIndex, 0, 3);
table.SetAlignment(rowIndex, 0, CellAlignment.MiddleCenter);
table.SetTextString(rowIndex, 0, serialNumber.ToString());
// 材料名称列 - 居中对齐
table.SetTextHeight(rowIndex, 1, 3);
table.SetAlignment(rowIndex, 1, CellAlignment.MiddleCenter);
table.SetTextString(rowIndex, 1, info.MaterialName);
// 规格列
table.SetTextHeight(rowIndex, 2, 3);
table.SetAlignment(rowIndex, 2, CellAlignment.MiddleCenter);
table.SetTextString(rowIndex, 2, info.Specification);
// 使用部位列
table.SetTextHeight(rowIndex, 3, 3);
table.SetAlignment(rowIndex, 3, CellAlignment.MiddleCenter);
table.SetTextString(rowIndex, 3, info.PlacementType);
// 长度列
table.SetTextHeight(rowIndex, 4, 3);
table.SetAlignment(rowIndex, 4, CellAlignment.MiddleCenter);
table.SetTextString(rowIndex, 4, info.Length.ToString());
// 数量统计列
table.SetTextHeight(rowIndex, 5, 3);
table.SetAlignment(rowIndex, 5, CellAlignment.MiddleCenter);
table.SetTextString(rowIndex, 5, info.Count.ToString());
}
#region 步骤1:量取尺寸并记忆
private bool Step1_MeasureAndMemorize(Editor ed)
{
ed.WriteMessage("\n=== 步骤1:量取尺寸 ===");
PromptPointOptions ptOpts1 = new PromptPointOptions("\n指定第一点");
PromptPointResult pt1Res = ed.GetPoint(ptOpts1);
if (pt1Res.Status != PromptStatus.OK)
{
ed.WriteMessage("\n取消量取操作");
return false;
}
PromptDistanceOptions distOpts = new PromptDistanceOptions("\n指定第二点(量取尺寸)");
distOpts.UseBasePoint = true;
distOpts.BasePoint = pt1Res.Value;
PromptDoubleResult distRes = ed.GetDistance(distOpts);
if (distRes.Status != PromptStatus.OK)
{
ed.WriteMessage("\n取消量取操作");
return false;
}
_memorizedDistance = distRes.Value;
ed.WriteMessage($"\n量取尺寸记忆成功:{_memorizedDistance:F2}mm");
return true;
}
#endregion
#region 步骤2:框选横杆、识别方向
private bool Step2_SelectBeamAndMemorize(Editor ed, Database db)
{
ed.WriteMessage("\n\n=== 步骤2:识别横杆 ===");
// 创建过滤器,只选择横杆图块
TypedValue[] filterList = new TypedValue[] {
new TypedValue((int)DxfCode.Start, "INSERT"),
new TypedValue((int)DxfCode.BlockName, "ScaffoldPole横杆*")
};
SelectionFilter filter = new SelectionFilter(filterList);
// 允许用户多次尝试选择
int maxAttempts = 3;
for (int attempt = 1; attempt <= maxAttempts; attempt++)
{
ed.WriteMessage($"\n\n请框选横杆图块 (尝试 {attempt}/{maxAttempts}):");
PromptSelectionResult selRes = ed.GetSelection(filter);
if (selRes.Status != PromptStatus.OK)
{
if (selRes.Status == PromptStatus.Cancel)
{
ed.WriteMessage("\n取消选择操作");
return false;
}
ed.WriteMessage("\n未选中任何对象,请重试");
continue;
}
if (selRes.Value.Count == 0)
{
ed.WriteMessage("\n未选中任何对象,请重试");
continue;
}
// 在事务中检查选中的对象
using (Transaction tr = db.TransactionManager.StartTransaction())
{
// 清空之前的横杆信息
_memorizedBeams.Clear();
// 查找所有有效的横杆图块
foreach (SelectedObject selObj in selRes.Value)
{
Entity ent = tr.GetObject(selObj.ObjectId, OpenMode.ForRead) as Entity;
if (ent is BlockReference)
{
BlockReference br = ent as BlockReference;
if (_beamBlockNames.Contains(br.Name))
{
// 使用旋转角度计算方向,而不是包围盒
Vector3d direction = Vector3d.XAxis.RotateBy(br.Rotation, Vector3d.ZAxis);
// 从块名中提取长度
string lengthStr = br.Name.Replace("ScaffoldPole横杆", "").Replace("mm", "");
double length = 0;
double.TryParse(lengthStr, out length);
_memorizedBeams.Add(new BeamInfo
{
Position = br.Position,
Rotation = br.Rotation,
Direction = direction,
Length = length,
BlockName = br.Name
});
// 记录第一根横杆的方向作为参考方向
if (_memorizedBeams.Count == 1)
{
_memorizedDirection = direction;
}
}
}
}
if (_memorizedBeams.Count == 0)
{
ed.WriteMessage("\n未选中有效横杆图块,请重试");
continue;
}
ed.WriteMessage($"\n成功识别 {_memorizedBeams.Count} 根横杆");
tr.Commit();
return true;
}
}
ed.WriteMessage($"\n经过{maxAttempts}次尝试后仍未选中有效横杆,命令终止");
return false;
}
#endregion
#region 步骤3:输入参数并布置方钢
private void Step3_InputParamsAndPlaceSteel(Editor ed, Database db)
{
ed.WriteMessage("\n\n=== 步骤3:布置方钢 ===");
// 首先获取布置基点
PromptPointOptions basePtOpts = new PromptPointOptions("\n指定布置基点");
PromptPointResult basePtRes = ed.GetPoint(basePtOpts);
if (basePtRes.Status != PromptStatus.OK)
{
ed.WriteMessage("\n取消布置操作");
return;
}
Point3d referencePoint = basePtRes.Value;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
try
{
// 1. 输入布置数量(使用整数选择)
PromptIntegerOptions countOpts = new PromptIntegerOptions("\n布置数量 [1(单根)/2(双拼)] ");
countOpts.AllowZero = false;
countOpts.AllowNegative = false;
countOpts.LowerLimit = 1;
countOpts.UpperLimit = 2;
countOpts.DefaultValue = 1;
PromptIntegerResult countRes = ed.GetInteger(countOpts);
if (countRes.Status != PromptStatus.OK)
{
ed.WriteMessage("\n取消布置操作");
return;
}
int placementCount = countRes.Value;
string placementType = (placementCount == 1) ? "单根" : "双拼";
// 2. 计算推荐的方钢长度
double recommendedLength = CalculateRecommendedSteelLength(_memorizedDistance);
// 3. 输入方钢长度(使用循环确保输入在有效范围内)
PromptDoubleOptions lenOpts = new PromptDoubleOptions($"\n(标准规格:1m,1.5m,2m,2.5m,3m,3.5m,4m,4.5m,6m)输入方钢长(mm)");
lenOpts.AllowNegative = false;
lenOpts.DefaultValue = recommendedLength; // 设置默认值为推荐长度
lenOpts.UseDefaultValue = true; // 使用默认值
PromptDoubleResult lenRes = ed.GetDouble(lenOpts);
if (lenRes.Status != PromptStatus.OK)
{
ed.WriteMessage("\n取消布置操作");
return;
}
double steelLength = lenRes.Value;
// 检查输入是否在有效范围内
if (steelLength < 500 || steelLength > 10000)
{
ed.WriteMessage("\n输入长度必须在500mm到10000mm之间,请重新输入。");
// 这里可以添加重新输入的逻辑,但为了简化,我们直接使用推荐长度
steelLength = recommendedLength;
ed.WriteMessage($"\n已自动使用推荐长度:{steelLength}mm");
}
if (!_steelStandardLengths.Contains(steelLength))
{
ed.WriteMessage($"\n注意:输入长度{steelLength}mm非标准规格,将按输入值拉伸");
}
// 4. 输入正负偏移值
PromptDoubleOptions offsetOpts = new PromptDoubleOptions("\n输入基点偏移值(mm,正值沿横杆方向,负值反向)");
PromptDoubleResult offsetRes = ed.GetDouble(offsetOpts);
if (offsetRes.Status != PromptStatus.OK)
{
ed.WriteMessage("\n取消布置操作");
return;
}
double offsetValue = offsetRes.Value;
// 5. 生成块名
string steelBlockName = GenerateSteelBlockName(steelLength);
// 6. 加载方钢图块
if (!LoadSteelBlock(db, tr, steelBlockName, steelLength))
{
ed.WriteMessage("\n方钢图块加载失败,终止布置");
return;
}
// 7. 创建图层(如果不存在)
string layerName = $"{_currentSettings.PlacementType}{steelLength / 1000}m";
CreateLayerIfNotExists(db, tr, layerName);
// 8. 计算所有横杆的插入点,确保端头对齐
List<Point3d> insertionPoints = new List<Point3d>();
// 首先计算参考点在横杆方向上的投影
double referenceProjection = 0;
if (_memorizedBeams.Count > 0)
{
Vector3d toReference = referencePoint - _memorizedBeams[0].Position;
referenceProjection = toReference.DotProduct(_memorizedDirection);
}
// 计算所有横杆的插入点
foreach (BeamInfo beam in _memorizedBeams)
{
// 计算横杆到参考点的投影距离
Vector3d toReference = referencePoint - beam.Position;
double projection = toReference.DotProduct(_memorizedDirection);
// 计算实际插入点,应用偏移值
Point3d actualInsertPoint = beam.Position + _memorizedDirection * (referenceProjection + offsetValue);
insertionPoints.Add(actualInsertPoint);
}
// 9. 为每根横杆布置方钢
int placedCount = 0;
for (int i = 0; i < _memorizedBeams.Count; i++)
{
BeamInfo beam = _memorizedBeams[i];
Point3d actualInsertPoint = insertionPoints[i];
double scaleFactor = steelLength / 1000.0; // 修正:正确的缩放因子计算
double rotationAngle = beam.Rotation;
// 根据设置的方向调整旋转角度 - 修正后的逻辑
switch (_currentSettings.Direction)
{
case "朝右":
// 不作任何改变
break;
case "朝左":
// 旋转180度
rotationAngle += Math.PI;
break;
case "朝上":
// 旋转90度
rotationAngle +=0/ Math.PI / 3;
break;
case "朝下":
// 旋转270度(即-90度)
rotationAngle += 3 * Math.PI / 3;
break;
}
// 布置方钢
if (placementCount == 1) // 单根
{
// 修正:单根布置时,方钢居中在横杆上
InsertSteelBlock(tr, db, steelBlockName, actualInsertPoint, scaleFactor, rotationAngle, beam.Direction, layerName, steelLength, placementCount, 0);
placedCount++;
}
else if (placementCount == 2) // 双拼
{
// 修正:双拼布置时,两根方钢对称布置在横杆两侧
Vector3d verticalDir = beam.Direction.RotateBy(Math.PI / 2, Vector3d.ZAxis);
double steelWidth = _currentSettings.Width;
// 第一根方钢(上方/右侧)
Point3d pt1 = actualInsertPoint + verticalDir * (steelWidth / 2);
InsertSteelBlock(tr, db, steelBlockName, pt1, scaleFactor, rotationAngle, beam.Direction, layerName, steelLength, placementCount, 1);
// 第二根方钢(下方/左侧)
Point3d pt2 = actualInsertPoint - verticalDir * (steelWidth / 2);
InsertSteelBlock(tr, db, steelBlockName, pt2, scaleFactor, rotationAngle, beam.Direction, layerName, steelLength, placementCount, 2);
placedCount += 2;
}
}
ed.WriteMessage($"\n方钢布置完成!共布置{placedCount}根方钢,长度{steelLength}mm,偏移{offsetValue}mm");
tr.Commit();
}
catch (System.Exception ex)
{
ed.WriteMessage($"\n布置过程错误:{ex.Message}");
tr.Abort();
}
}
}
#endregion
#region 辅助方法:计算推荐的方钢长度
private double CalculateRecommendedSteelLength(double measuredDistance)
{
// 如果量取尺寸大于6000mm,则不扣减,默认使用6000mm
if (measuredDistance > 6000)
{
return 6000;
}
// 否则扣减350mm
double deductedDistance = measuredDistance - 350;
// 在标准规格中找到最接近但不大于扣减后尺寸的值
double recommendedLength = 1000; // 默认最小值
foreach (double standardLength in _steelStandardLengths)
{
if (standardLength <= deductedDistance && standardLength > recommendedLength)
{
recommendedLength = standardLength;
}
}
return recommendedLength;
}
#endregion
#region 双槽钢拖梁布置命令(缩写SC1)
[CommandMethod("ScaffoldChannel", "SC1", CommandFlags.Modal)]
public void ScaffoldChannelSteelPlacement()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
Database db = doc.Database;
try
{
ed.WriteMessage("\n=== 双槽钢/梁下方钢布置 ===");
// 框选横杆
if (!Step2_SelectBeamAndMemorize(ed, db)) return;
// 选择布置类型
PromptKeywordOptions typeOpts = new PromptKeywordOptions("\n请选择布置类型 [<1>梁下方钢/<2>双槽钢拖梁]");
typeOpts.Keywords.Add("1");
typeOpts.Keywords.Add("2");
typeOpts.Keywords.Default = "1";
typeOpts.AllowNone = false;
PromptResult typeRes = ed.GetKeywords(typeOpts);
if (typeRes.Status != PromptStatus.OK)
{
ed.WriteMessage("\n取消布置操作");
return;
}
string selectedType = typeRes.StringResult;
if (selectedType == "1")
{
// 梁下方钢布置逻辑
// 1. 输入布置数量
PromptIntegerOptions countOpts = new PromptIntegerOptions("\n布置数量 [1(单根)/2(双拼)] ");
countOpts.AllowZero = false;
countOpts.AllowNegative = false;
countOpts.LowerLimit = 1;
countOpts.UpperLimit = 2;
countOpts.DefaultValue = 1;
PromptIntegerResult countRes = ed.GetInteger(countOpts);
if (countRes.Status != PromptStatus.OK)
{
ed.WriteMessage("\n取消布置操作");
return;
}
int placementCount = countRes.Value;
// 2. 输入方钢长度
PromptDoubleOptions lenOpts = new PromptDoubleOptions($"\n输入方钢长度(mm)");
lenOpts.AllowNegative = false;
lenOpts.DefaultValue = 1000; // 默认1000mm
lenOpts.UseDefaultValue = true;
PromptDoubleResult lenRes = ed.GetDouble(lenOpts);
if (lenRes.Status != PromptStatus.OK)
{
ed.WriteMessage("\n取消布置操作");
return;
}
double steelLength = lenRes.Value;
// 3. 加载梁下方钢图块
string blockName = $"ScaffoldPole方钢{steelLength}mm";
using (Transaction tr = db.TransactionManager.StartTransaction())
{
// 加载图块
if (!LoadSteelBlock(db, tr, blockName, steelLength))
{
ed.WriteMessage("\n梁下方钢图块加载失败,终止布置");
return;
}
// 创建图层(如果不存在)
string layerName = $"梁下方钢{steelLength / 1000}m";
CreateLayerIfNotExists(db, tr, layerName);
// 布置梁下方钢
int placedCount = 0;
BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
foreach (BeamInfo beam in _memorizedBeams)
{
// 计算插入点(横杆的中心点)
Point3d insertPoint = beam.Position;
double rotationAngle = beam.Rotation;
// 计算缩放比例
double scaleFactor = steelLength / 1000.0; // 基础图块长度为1000mm
if (placementCount == 1) // 单根
{
// 插入梁下方钢图块
BlockReference steelRef = new BlockReference(insertPoint, bt[blockName]);
steelRef.Rotation = rotationAngle;
steelRef.ScaleFactors = new Scale3d(scaleFactor, 1, 1); // 只拉伸长度,宽度不变
steelRef.Layer = layerName;
steelRef.ColorIndex = 1; // 使用不同颜色区分
btr.AppendEntity(steelRef);
tr.AddNewlyCreatedDBObject(steelRef, true);
placedCount++;
}
else if (placementCount == 2) // 双拼
{
Vector3d verticalDir = beam.Direction.RotateBy(Math.PI / 2, Vector3d.ZAxis);
double steelWidth = 50; // 方钢宽度50mm
// 第一根方钢(上方/右侧)
Point3d pt1 = insertPoint + verticalDir * (steelWidth / 2);
BlockReference steelRef1 = new BlockReference(pt1, bt[blockName]);
steelRef1.Rotation = rotationAngle;
steelRef1.ScaleFactors = new Scale3d(scaleFactor, 1, 1);
steelRef1.Layer = layerName;
steelRef1.ColorIndex = 1;
// 第二根方钢(下方/左侧)
Point3d pt2 = insertPoint - verticalDir * (steelWidth / 2);
BlockReference steelRef2 = new BlockReference(pt2, bt[blockName]);
steelRef2.Rotation = rotationAngle;
steelRef2.ScaleFactors = new Scale3d(scaleFactor, 1, 1);
steelRef2.Layer = layerName;
steelRef2.ColorIndex = 1;
btr.AppendEntity(steelRef1);
tr.AddNewlyCreatedDBObject(steelRef1, true);
btr.AppendEntity(steelRef2);
tr.AddNewlyCreatedDBObject(steelRef2, true);
placedCount += 2;
}
}
ed.WriteMessage($"\n梁下方钢布置完成!共布置{placedCount}根方钢,长度{steelLength}mm");
tr.Commit();
}
}
else // 双槽钢拖梁
{
// 选择双槽钢拖梁长度
PromptKeywordOptions lengthOpts = new PromptKeywordOptions("\n请选择双槽钢拖梁长度");
lengthOpts.Keywords.Add("1350mm");
lengthOpts.Keywords.Add("1650mm");
lengthOpts.Keywords.Default = "1350mm";
lengthOpts.AllowNone = false;
PromptResult lengthRes = ed.GetKeywords(lengthOpts);
if (lengthRes.Status != PromptStatus.OK)
{
ed.WriteMessage("\n取消布置操作");
return;
}
string selectedLength = lengthRes.StringResult;
double channelLength = selectedLength == "1350mm" ? 1350 : 1650;
string blockName = $"ScaffoldPole双槽钢拖梁{selectedLength}";
using (Transaction tr = db.TransactionManager.StartTransaction())
{
// 加载双槽钢拖梁图块
if (!LoadChannelSteelBlock(db, tr, blockName, channelLength))
{
ed.WriteMessage("\n双槽钢拖梁图块加载失败,终止布置");
return;
}
// 创建图层(如果不存在)
string layerName = $"双槽钢拖梁{channelLength / 1000}m";
CreateLayerIfNotExists(db, tr, layerName);
// 布置双槽钢拖梁
int placedCount = 0;
BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
foreach (BeamInfo beam in _memorizedBeams)
{
// 计算双槽钢拖梁的插入点(横杆的中心点)
Point3d insertPoint = beam.Position;
// 使用横杆的旋转角度,使双槽钢拖梁与横杆平行
double rotationAngle = beam.Rotation;
// 插入双槽钢拖梁图块
BlockReference channelRef = new BlockReference(insertPoint, bt[blockName]);
channelRef.Rotation = rotationAngle;
channelRef.Layer = layerName;
channelRef.ColorIndex = 3; // 使用不同颜色区分
btr.AppendEntity(channelRef);
tr.AddNewlyCreatedDBObject(channelRef, true);
placedCount++;
}
ed.WriteMessage($"\n双槽钢拖梁布置完成!共布置{placedCount}根双槽钢拖梁,长度{channelLength}mm");
tr.Commit();
}
}
}
catch (System.Exception ex)
{
ed.WriteMessage($"\n布置过程错误:{ex.Message}");
}
}
#endregion
#region 辅助方法:生成方钢块名
private string GenerateSteelBlockName(double steelLength)
{
// 将毫米转换为米,并保留一位小数
double lengthInMeters = steelLength / 1000.0;
return $"{lengthInMeters:F1}m方钢";
}
#endregion
#region 辅助方法:加载方钢图块(已删除文字标注)
private bool LoadSteelBlock(Database db, Transaction tr, string blockName, double length)
{
BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
if (bt.Has(blockName)) return true;
try
{
bt.UpgradeOpen();
BlockTableRecord btr = new BlockTableRecord();
btr.Name = blockName;
ObjectId blockId = bt.Add(btr);
tr.AddNewlyCreatedDBObject(btr, true);
double halfWidth = _currentSettings.Width / 2;
// 1. 创建方钢几何图形
using (Polyline steelPolyline = new Polyline())
{
steelPolyline.AddVertexAt(0, new Point2d(0, -halfWidth), 0, 0, 0);
steelPolyline.AddVertexAt(1, new Point2d(length, -halfWidth), 0, 0, 0);
steelPolyline.AddVertexAt(2, new Point2d(length, halfWidth), 0, 0, 0);
steelPolyline.AddVertexAt(3, new Point2d(0, halfWidth), 0, 0, 0);
steelPolyline.Closed = true;
steelPolyline.ColorIndex = 2; // 黄色
btr.AppendEntity(steelPolyline);
tr.AddNewlyCreatedDBObject(steelPolyline, true);
}
// 注意:已删除文字标注部分,不再创建DBText对象
bt.DowngradeOpen();
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
ed.WriteMessage($"\n创建图块 '{blockName}',长度: {length}mm,图块原点在方钢中点");
return true;
}
catch (System.Exception ex)
{
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
ed.WriteMessage($"\n创建方钢图块时出错:{ex.Message}");
return false;
}
}
#endregion
#region 辅助方法:插入方钢图块 - 修正基点问题
private void InsertSteelBlock(Transaction tr, Database db, string blockName, Point3d insertPt, double rotation, Vector3d direction, string layerName, double steelLength, int placementCount, int positionIndex)
{
BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
BlockReference steelRef = new BlockReference(insertPt, bt[blockName]);
// 修正:图块原点在左端点,所以不需要额外的偏移计算
// 只需要设置旋转角度和缩放因子
double widthScale = _currentSettings.Width / 50.0; // 默认宽度为50mm
steelRef.ScaleFactors = new Scale3d(1, widthScale, 1);
steelRef.Rotation = rotation;
steelRef.ColorIndex = 2;
// 设置图层
steelRef.Layer = layerName;
// 添加扩展数据,记录规格和使用部位信息
AddXData(tr, steelRef, _currentSettings.Height, _currentSettings.Width, _currentSettings.PlacementType, steelLength);
btr.AppendEntity(steelRef);
tr.AddNewlyCreatedDBObject(steelRef, true);
}
#endregion
#region 辅助方法:加载双槽钢拖梁图块
private bool LoadChannelSteelBlock(Database db, Transaction tr, string blockName, double length)
{
BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
// 检查是否已存在同名块
if (bt.Has(blockName))
{
return true;
}
// 创建新的双槽钢拖梁图块定义
try
{
bt.UpgradeOpen();
// 创建新的块表记录
BlockTableRecord btr = new BlockTableRecord();
btr.Name = blockName;
// 将新块添加到块表
ObjectId blockId = bt.Add(btr);
tr.AddNewlyCreatedDBObject(btr, true);
// 创建双槽钢拖梁的几何图形(两个C型槽钢)
double channelHeight = 100; // 槽钢高度
double channelWidth = 48; // 槽钢宽度
double flangeThickness = 8; // 翼缘厚度
double webThickness = 5; // 腹板厚度
// 第一个槽钢
using (Polyline channel1 = CreateChannelProfile(-length / 2, 0, length, channelHeight, channelWidth, flangeThickness, webThickness))
{
channel1.ColorIndex = 1; // 红色
btr.AppendEntity(channel1);
tr.AddNewlyCreatedDBObject(channel1, true);
}
// 第二个槽钢(与第一个并排)
using (Polyline channel2 = CreateChannelProfile(-length / 2, channelWidth + 10, length, channelHeight, channelWidth, flangeThickness, webThickness))
{
channel2.ColorIndex = 1; // 红色
btr.AppendEntity(channel2);
tr.AddNewlyCreatedDBObject(channel2, true);
}
// 添加文字标注
using (DBText text = new DBText())
{
text.TextString = $"双槽钢拖梁 {length}mm";
text.Position = new Point3d(0, channelWidth * 2 + 20, 0);
text.Height = 15;
text.HorizontalMode = TextHorizontalMode.TextCenter;
text.VerticalMode = TextVerticalMode.TextBase;
text.ColorIndex = 3; // 绿色
btr.AppendEntity(text);
tr.AddNewlyCreatedDBObject(text, true);
}
bt.DowngradeOpen();
return true;
}
catch (System.Exception ex)
{
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
ed.WriteMessage($"\n创建双槽钢拖梁图块时出错:{ex.Message}");
return false;
}
}
#endregion
#region 辅助方法:创建槽钢轮廓
private Polyline CreateChannelProfile(double startX, double startY, double length, double height, double width, double flangeThickness, double webThickness)
{
Polyline channel = new Polyline();
// 创建C型槽钢轮廓
channel.AddVertexAt(0, new Point2d(startX, startY), 0, 0, 0);
channel.AddVertexAt(1, new Point2d(startX + length, startY), 0, 0, 0);
channel.AddVertexAt(2, new Point2d(startX + length, startY + flangeThickness), 0, 0, 0);
channel.AddVertexAt(3, new Point2d(startX + webThickness, startY + flangeThickness), 0, 0, 0);
channel.AddVertexAt(4, new Point2d(startX + webThickness, startY + height - flangeThickness), 0, 0, 0);
channel.AddVertexAt(5, new Point2d(startX + length, startY + height - flangeThickness), 0, 0, 0);
channel.AddVertexAt(6, new Point2d(startX + length, startY + height), 0, 0, 0);
channel.AddVertexAt(7, new Point2d(startX, startY + height), 0, 0, 0);
channel.AddVertexAt(8, new Point2d(startX, startY + height - flangeThickness), 0, 0, 0);
channel.AddVertexAt(9, new Point2d(startX + width - webThickness, startY + height - flangeThickness), 0, 0, 0);
channel.AddVertexAt(10, new Point2d(startX + width - webThickness, startY + flangeThickness), 0, 0, 0);
channel.AddVertexAt(11, new Point2d(startX, startY + flangeThickness), 0, 0, 0);
channel.Closed = true;
return channel;
}
#endregion
#region 辅助方法:创建图层(如果不存在)
private void CreateLayerIfNotExists(Database db, Transaction tr, string layerName)
{
LayerTable lt = (LayerTable)tr.GetObject(db.LayerTableId, OpenMode.ForRead);
if (!lt.Has(layerName))
{
lt.UpgradeOpen();
LayerTableRecord ltr = new LayerTableRecord();
ltr.Name = layerName;
lt.Add(ltr);
tr.AddNewlyCreatedDBObject(ltr, true);
lt.DowngradeOpen();
}
}
#endregion
#region 辅助方法:插入方钢图块 - 修正版本
private void InsertSteelBlock(Transaction tr, Database db, string blockName, Point3d insertPt, double scale, double rotation, Vector3d direction, string layerName, double steelLength, int placementCount, int positionIndex)
{
BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
BlockReference steelRef = new BlockReference(insertPt, bt[blockName]);
// 修正:正确的缩放因子计算
// 由于图块现在以原点为中心创建,缩放因子应该是1(不缩放),因为长度已经在创建图块时确定了
// 但我们仍然使用缩放来调整宽度,如果需要的话
double widthScale = _currentSettings.Width / 50.0; // 默认宽度为50mm
steelRef.ScaleFactors = new Scale3d(1, widthScale, 1); // 修正:x方向缩放为1,因为长度已在图块创建时确定
steelRef.Rotation = rotation;
steelRef.ColorIndex = 2;
// 设置图层
steelRef.Layer = layerName;
// 添加扩展数据,记录规格和使用部位信息
AddXData(tr, steelRef, _currentSettings.Height, _currentSettings.Width, _currentSettings.PlacementType, steelLength);
btr.AppendEntity(steelRef);
tr.AddNewlyCreatedDBObject(steelRef, true);
}
#endregion
#region 辅助方法:添加扩展数据
private void AddXData(Transaction tr, BlockReference blockRef, double height, double width, string placementType, double steelLength)
{
try
{
// 首先确保应用程序名称已注册
RegAppTable rat = (RegAppTable)tr.GetObject(blockRef.Database.RegAppTableId, OpenMode.ForRead);
if (!rat.Has("SCAFFOLD_STEEL"))
{
rat.UpgradeOpen();
RegAppTableRecord ratr = new RegAppTableRecord();
ratr.Name = "SCAFFOLD_STEEL";
rat.Add(ratr);
tr.AddNewlyCreatedDBObject(ratr, true);
rat.DowngradeOpen();
}
// 创建扩展数据
ResultBuffer rb = new ResultBuffer();
// 添加应用程序名称
rb.Add(new TypedValue(1001, "SCAFFOLD_STEEL"));
// 添加高度信息
rb.Add(new TypedValue(1040, height));
// 添加宽度信息
rb.Add(new TypedValue(1040, width));
// 添加使用部位信息
rb.Add(new TypedValue(1000, placementType));
// 添加长度信息
rb.Add(new TypedValue(1040, steelLength));
// 将扩展数据附加到图块
blockRef.XData = rb;
}
catch (System.Exception ex)
{
// 扩展数据设置失败不影响主要功能
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
ed.WriteMessage($"\n设置扩展数据时出错:{ex.Message}");
}
}
#endregion
}
// 参数设置窗体
public class SettingsForm : Form
{
private RadioButton radioUnderSlab;
private RadioButton radioUnderBeam;
private TextBox textBoxHeight;
private TextBox textBoxWidth;
private ComboBox comboBoxDirection;
private Button buttonOK;
private Button buttonCancel;
public SettingsForm()
{
InitializeComponent();
}
private void InitializeComponent()
{
this.Text = "方钢布置参数设置";
this.Size = new System.Drawing.Size(300, 250);
this.StartPosition = FormStartPosition.CenterScreen;
this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
// 板下方钢布置单选按钮
radioUnderSlab = new RadioButton();
radioUnderSlab.Text = "板下方钢布置";
radioUnderSlab.Location = new System.Drawing.Point(20, 20);
radioUnderSlab.Size = new System.Drawing.Size(120, 20);
radioUnderSlab.Checked = true;
radioUnderSlab.CheckedChanged += RadioUnderSlab_CheckedChanged;
this.Controls.Add(radioUnderSlab);
// 梁下方钢布置单选按钮
radioUnderBeam = new RadioButton();
radioUnderBeam.Text = "梁下方钢布置";
radioUnderBeam.Location = new System.Drawing.Point(20, 50);
radioUnderBeam.Size = new System.Drawing.Size(120, 20);
radioUnderBeam.CheckedChanged += RadioUnderBeam_CheckedChanged;
this.Controls.Add(radioUnderBeam);
// 高度标签和文本框
Label labelHeight = new Label();
labelHeight.Text = "高:";
labelHeight.Location = new System.Drawing.Point(40, 80);
labelHeight.Size = new System.Drawing.Size(30, 20);
this.Controls.Add(labelHeight);
textBoxHeight = new TextBox();
textBoxHeight.Location = new System.Drawing.Point(70, 80);
textBoxHeight.Size = new System.Drawing.Size(60, 20);
textBoxHeight.Text = "100";
this.Controls.Add(textBoxHeight);
// 宽度标签和文本框
Label labelWidth = new Label();
labelWidth.Text = "宽:";
labelWidth.Location = new System.Drawing.Point(140, 80);
labelWidth.Size = new System.Drawing.Size(30, 20);
this.Controls.Add(labelWidth);
textBoxWidth = new TextBox();
textBoxWidth.Location = new System.Drawing.Point(170, 80);
textBoxWidth.Size = new System.Drawing.Size(60, 20);
textBoxWidth.Text = "50";
this.Controls.Add(textBoxWidth);
// 布置方向标签和下拉框
Label labelDirection = new Label();
labelDirection.Text = "布置方向:";
labelDirection.Location = new System.Drawing.Point(40, 110);
labelDirection.Size = new System.Drawing.Size(80, 20);
this.Controls.Add(labelDirection);
comboBoxDirection = new ComboBox();
comboBoxDirection.Location = new System.Drawing.Point(120, 110);
comboBoxDirection.Size = new System.Drawing.Size(100, 20);
comboBoxDirection.Items.AddRange(new object[] { "朝上", "朝下", "朝左", "朝右" });
comboBoxDirection.SelectedIndex = 0;
this.Controls.Add(comboBoxDirection);
// 确定按钮
buttonOK = new Button();
buttonOK.Text = "开始布置";
buttonOK.Location = new System.Drawing.Point(50, 150);
buttonOK.Size = new System.Drawing.Size(80, 25);
buttonOK.DialogResult = DialogResult.OK;
this.Controls.Add(buttonOK);
// 取消按钮
buttonCancel = new Button();
buttonCancel.Text = "取消";
buttonCancel.Location = new System.Drawing.Point(150, 150);
buttonCancel.Size = new System.Drawing.Size(80, 25);
buttonCancel.DialogResult = DialogResult.Cancel;
this.Controls.Add(buttonCancel);
// 初始化状态
UpdateControlsState();
}
private void RadioUnderSlab_CheckedChanged(object sender, EventArgs e)
{
if (radioUnderSlab.Checked)
{
UpdateControlsState();
}
}
private void RadioUnderBeam_CheckedChanged(object sender, EventArgs e)
{
if (radioUnderBeam.Checked)
{
UpdateControlsState();
}
}
private void UpdateControlsState()
{
// 根据选择更新控件状态
bool isUnderSlab = radioUnderSlab.Checked;
// 这里可以根据需要添加更多逻辑
}
public ScaffoldSteelPlugin.ScaffoldCommands.SteelSettings GetSettings()
{
return new ScaffoldSteelPlugin.ScaffoldCommands.SteelSettings
{
IsUnderSlab = radioUnderSlab.Checked,
IsUnderBeam = radioUnderBeam.Checked,
Height = double.Parse(textBoxHeight.Text),
Width = double.Parse(textBoxWidth.Text),
Direction = comboBoxDirection.SelectedItem.ToString(),
PlacementType = radioUnderSlab.Checked ? "板下方钢" : "梁下方钢"
};
}
}
}
"SP"方钢布置时自动在方钢的中间标注尺寸,标注样式为:50x100x3方钢*m