using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.GraphicsInterface;
using Autodesk.AutoCAD.Runtime;
using System;
using System.Collections.Generic;
using System.Linq;
// 解决Exception命名冲突
using SysException = System.Exception;
using AcException = Autodesk.AutoCAD.Runtime.Exception;
namespace GridLinePlugin
{
public class GridLineCommands
{
// 固定线段长度选项(毫米)
private static readonly double[] FixedLengths = { 300, 600, 900, 1200, 1500, 1800 };
private const string LAYER_NAME = "盘扣轴网";
// 参数记忆静态变量
private static double _lastOffsetX = 0.0;
private static double _lastOffsetY = 0.0;
private static double _lastStartLengthX = 900.0;
private static double _lastStartLengthY = 1500.0;
// 辅助方法移动到类顶部
private double GetOffset(string message, double defaultValue, Editor ed)
{
string formattedMessage = string.Format(message, defaultValue);
PromptDoubleOptions opts = new PromptDoubleOptions(formattedMessage)
{
AllowNegative = true,
AllowZero = true,
DefaultValue = defaultValue,
UseDefaultValue = true
};
PromptDoubleResult res = ed.GetDouble(opts);
return res.Status == PromptStatus.OK ? res.Value : defaultValue;
}
private double GetSegmentLength(string message, double defaultValue, Editor ed)
{
string formattedMessage = string.Format(message, defaultValue);
PromptDoubleOptions opts = new PromptDoubleOptions(formattedMessage)
{
AllowNegative = false,
AllowZero = false,
AllowNone = false,
DefaultValue = defaultValue,
UseDefaultValue = true
};
PromptDoubleResult res;
do
{
res = ed.GetDouble(opts);
if (res.Status != PromptStatus.OK) return defaultValue;
if (res.Value <= 0)
{
ed.WriteMessage("\n长度必须大于0! 请重新输入: ");
}
} while (res.Value <= 0);
return res.Value;
}
[CommandMethod("DG", CommandFlags.Modal)]
public void DrawGridInteractive()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
try
{
// 1. 获取偏移值
double offsetX = GetOffset("\nX方向偏移值 (mm) <{0}>: ", _lastOffsetX, ed);
double offsetY = GetOffset("\nY方向偏移值 (mm) <{0}>: ", _lastOffsetY, ed);
// 2. 获取起始长度
double startLengthX = GetSegmentLength("\n横向起始线段长度 (mm) <{0}>: ", _lastStartLengthX, ed);
double startLengthY = GetSegmentLength("\n纵向起始线段长度 (mm) <{0}>: ", _lastStartLengthY, ed);
// 更新记忆参数
_lastOffsetX = offsetX;
_lastOffsetY = offsetY;
_lastStartLengthX = startLengthX;
_lastStartLengthY = startLengthY;
// 3. 获取起始点
PromptPointResult startPtRes = ed.GetPoint("\n选择网格起始点: ");
if (startPtRes.Status != PromptStatus.OK) return;
Point3d basePoint = startPtRes.Value;
// 应用偏移
Point3d actualStartPoint = basePoint + new Vector3d(offsetX, offsetY, 0);
// 4. 创建并运行拖拽器
GridJig jig = new GridJig(actualStartPoint, startLengthX, startLengthY, doc);
PromptResult jigResult = ed.Drag(jig);
if (jigResult.Status == PromptStatus.OK)
{
jig.CommitGrid(LAYER_NAME);
int shortLines = jig.GetShortLineCount(300.0);
if (shortLines > 0)
{
ed.WriteMessage($"\n警告: 有 {shortLines} 条线段长度小于300mm!");
}
ed.WriteMessage($"\n成功绘制网格! 共生成 {jig.LineCount} 条线段");
}
}
catch (SysException ex) // 使用别名解决冲突
{
ed.WriteMessage($"\n错误: {ex.Message}");
}
}
[CommandMethod("CO", CommandFlags.Modal)]
public void CheckOverlap()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
try
{
// 让用户框选线段
PromptSelectionOptions selOpts = new PromptSelectionOptions
{
MessageForAdding = "请框选需要检查重叠的线段: "
};
PromptSelectionResult selRes = ed.GetSelection(selOpts);
if (selRes.Status != PromptStatus.OK) return;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTableRecord btr = tr.GetObject(
SymbolUtilityServices.GetBlockModelSpaceId(db),
OpenMode.ForWrite) as BlockTableRecord;
// 收集选中的线段
List<Line> allLines = new List<Line>();
foreach (SelectedObject so in selRes.Value)
{
Line line = tr.GetObject(so.ObjectId, OpenMode.ForRead) as Line;
if (line != null && line.Layer == LAYER_NAME)
{
allLines.Add(line);
}
}
// 分组处理:水平线和垂直线
List<Line> horizontalLines = new List<Line>();
List<Line> verticalLines = new List<Line>();
const double angleTolerance = 0.01; // 角度容差(弧度)
foreach (Line line in allLines)
{
Vector3d dir = (line.EndPoint - line.StartPoint).GetNormal();
double angle = dir.GetAngleTo(Vector3d.XAxis);
// 水平线(0°或180°)
if (angle < angleTolerance || Math.Abs(angle - Math.PI) < angleTolerance)
{
horizontalLines.Add(line);
}
// 垂直线(90°或270°)
else if (Math.Abs(angle - Math.PI / 2) < angleTolerance ||
Math.Abs(angle - 3 * Math.PI / 2) < angleTolerance)
{
verticalLines.Add(line);
}
}
// 处理水平线重叠 - 直接删除重叠线段(保留一条)
ProcessOverlappingLines(horizontalLines, true, tr);
// 处理垂直线重叠 - 直接删除重叠线段(保留一条)
ProcessOverlappingLines(verticalLines, false, tr);
tr.Commit();
ed.WriteMessage("\n重叠处理完成!");
}
}
catch (SysException ex)
{
ed.WriteMessage($"\n错误: {ex.Message}");
}
}
// 处理同一方向上的线段重叠 - 新方法:直接删除重叠线段(保留一条)
private void ProcessOverlappingLines(List<Line> lines, bool isHorizontal, Transaction tr)
{
if (lines.Count < 2) return;
// 按坐标排序(水平线按Y,垂直线按X)
lines.Sort((a, b) =>
isHorizontal ?
a.StartPoint.Y.CompareTo(b.StartPoint.Y) :
a.StartPoint.X.CompareTo(b.StartPoint.X));
// 分组:相同坐标位置的线段为一组
var groups = lines.GroupBy(line =>
isHorizontal ?
Math.Round(line.StartPoint.Y, 4) :
Math.Round(line.StartPoint.X, 4)
);
// 记录要删除的线段ID
List<ObjectId> linesToDelete = new List<ObjectId>();
foreach (var group in groups)
{
// 每组内按起点位置排序
List<Line> sortedLines = isHorizontal ?
group.OrderBy(l => Math.Min(l.StartPoint.X, l.EndPoint.X)).ToList() :
group.OrderBy(l => Math.Min(l.StartPoint.Y, l.EndPoint.Y)).ToList();
// 创建已检查线段的集合
HashSet<ObjectId> processedLines = new HashSet<ObjectId>();
for (int i = 0; i < sortedLines.Count; i++)
{
Line line1 = sortedLines[i];
if (processedLines.Contains(line1.ObjectId)) continue;
for (int j = i + 1; j < sortedLines.Count; j++)
{
Line line2 = sortedLines[j];
if (processedLines.Contains(line2.ObjectId)) continue;
// 获取线段范围
double start1, end1, start2, end2;
GetLineRange(line1, isHorizontal, out start1, out end1);
GetLineRange(line2, isHorizontal, out start2, out end2);
// 检查重叠
if (end1 > start2 && end2 > start1)
{
// 将第二条线段标记为删除
linesToDelete.Add(line2.ObjectId);
processedLines.Add(line2.ObjectId);
}
}
}
}
// 删除所有标记的线段
foreach (ObjectId id in linesToDelete)
{
Line line = tr.GetObject(id, OpenMode.ForWrite) as Line;
if (!line.IsErased)
{
line.Erase();
}
}
}
// 获取线段在指定方向的范围
private void GetLineRange(Line line, bool isHorizontal, out double start, out double end)
{
if (isHorizontal)
{
start = Math.Min(line.StartPoint.X, line.EndPoint.X);
end = Math.Max(line.StartPoint.X, line.EndPoint.X);
}
else
{
start = Math.Min(line.StartPoint.Y, line.EndPoint.Y);
end = Math.Max(line.StartPoint.Y, line.EndPoint.Y);
}
}
// 检查两条线是否重叠
private bool AreLinesOverlapping(Line line1, Line line2)
{
const double tolerance = 0.001;
Vector3d dir1 = line1.EndPoint - line1.StartPoint;
Vector3d dir2 = line2.EndPoint - line2.StartPoint;
if (!dir1.IsParallelTo(dir2, new Tolerance(tolerance, tolerance)))
return false;
Vector3d toLine2Start = line2.StartPoint - line1.StartPoint;
if (!toLine2Start.IsPerpendicularTo(dir1, new Tolerance(tolerance, tolerance)))
return false;
double min1 = Math.Min(0, 1);
double max1 = Math.Max(0, 1);
double startParam = GetLineParameter(line1, line2.StartPoint);
double endParam = GetLineParameter(line1, line2.EndPoint);
double min2 = Math.Min(startParam, endParam);
double max2 = Math.Max(startParam, endParam);
return (min1 <= max2 + tolerance && max1 >= min2 - tolerance);
}
// 计算点在直线上的参数位置
private double GetLineParameter(Line line, Point3d point)
{
Vector3d lineVec = line.EndPoint - line.StartPoint;
Vector3d pointVec = point - line.StartPoint;
double dotProduct = lineVec.DotProduct(pointVec);
double lineLengthSq = lineVec.DotProduct(lineVec);
return lineLengthSq > 0 ? dotProduct / lineLengthSq : 0;
}
// 网格拖拽器类
private class GridJig : DrawJig
{
private readonly Point3d _startPoint;
private Point3d _currentPoint;
private readonly double _startLengthX;
private readonly double _startLengthY;
private readonly Document _doc;
private readonly List<Line> _lines = new List<Line>();
private List<double> _xSegments = new List<double>();
private List<double> _ySegments = new List<double>();
public int LineCount => _lines.Count;
public int GetShortLineCount(double minLength)
{
return _lines.Count(line => line.Length < minLength);
}
public GridJig(Point3d startPoint, double startLengthX, double startLengthY, Document doc)
{
_startPoint = startPoint;
_currentPoint = startPoint;
_startLengthX = startLengthX;
_startLengthY = startLengthY;
_doc = doc;
}
// 实现抽象成员
protected override SamplerStatus Sampler(JigPrompts prompts)
{
JigPromptPointOptions opts = new JigPromptPointOptions("\n指定网格终点或输入长度: ")
{
UserInputControls = UserInputControls.Accept3dCoordinates |
UserInputControls.NoZeroResponseAccepted |
UserInputControls.NoNegativeResponseAccepted,
BasePoint = _startPoint,
UseBasePoint = true,
Cursor = CursorType.RubberBand
};
PromptPointResult result = prompts.AcquirePoint(opts);
if (result.Status == PromptStatus.Cancel)
return SamplerStatus.Cancel;
if (_currentPoint.DistanceTo(result.Value) < 0.001)
return SamplerStatus.NoChange;
_currentPoint = result.Value;
return SamplerStatus.OK;
}
protected override bool WorldDraw(WorldDraw draw)
{
try
{
// 清除临时图形
foreach (var line in _lines)
{
if (!line.IsDisposed) line.Dispose();
}
_lines.Clear();
// 计算方向向量
Vector3d delta = _currentPoint - _startPoint;
double dx = delta.X;
double dy = delta.Y;
// 计算总长度和方向
double totalX = Math.Abs(dx);
double totalY = Math.Abs(dy);
int signX = Math.Sign(dx);
int signY = Math.Sign(dy);
// 生成分段点 - 使用指定的起始长度
_xSegments = GenerateSegments(totalX, _startLengthX, true);
_ySegments = GenerateSegments(totalY, _startLengthY, false);
// 调试输出
_doc.Editor.WriteMessage($"\nX方向分段数: {_xSegments.Count}, Y方向分段数: {_ySegments.Count}");
// 计算实际终点
Point3d actualEndPoint = new Point3d(
_startPoint.X + signX * _xSegments.Last(),
_startPoint.Y + signY * _ySegments.Last(),
_startPoint.Z
);
// 绘制横向网格线
DrawHorizontalLines(signX, signY, actualEndPoint, draw);
// 绘制纵向网格线
DrawVerticalLines(signX, signY, actualEndPoint, draw);
}
catch (SysException ex)
{
_doc.Editor.WriteMessage($"\n绘图错误: {ex.Message}");
}
return true;
}
// 分段生成算法 - 修复:优先使用起始长度进行连续布置
private List<double> GenerateSegments(double totalLength, double spacing, bool isXDirection)
{
List<double> segments = new List<double> { 0 };
if (totalLength <= 0) return segments;
double currentPos = 0;
int segmentCount = 0;
// 优先使用起始长度进行连续布置
while (currentPos < totalLength && segmentCount < 100)
{
// 尝试使用起始长度
if (currentPos + spacing <= totalLength)
{
currentPos += spacing;
segments.Add(currentPos);
segmentCount++;
continue;
}
// 起始长度不适用时,尝试固定长度
double maxFixed = FixedLengths
.Where(len => currentPos + len <= totalLength)
.DefaultIfEmpty(0)
.Max();
if (maxFixed >= 300)
{
currentPos += maxFixed;
segments.Add(currentPos);
segmentCount++;
}
else
{
// 如果固定长度也不适用,尝试剩余长度
double remaining = totalLength - currentPos;
if (remaining >= 300)
{
currentPos = totalLength;
segments.Add(currentPos);
}
break;
}
}
// 确保包含终点(如果剩余长度不足300mm则跳过)
if (segments.Last() < totalLength && (totalLength - segments.Last()) >= 300)
{
segments.Add(totalLength);
}
// 确保至少有两个分段点
if (segments.Count < 2)
{
segments.Add(totalLength);
}
return segments;
}
// 绘制横向线
private void DrawHorizontalLines(int signX, int signY, Point3d actualEndPoint, WorldDraw draw)
{
if (_ySegments.Count == 0) return;
for (int i = 0; i < _ySegments.Count; i++)
{
double yPos = _ySegments[i];
Point3d start = _startPoint + new Vector3d(0, signY * yPos, 0);
// 终点自动修剪到实际终点
Point3d end = new Point3d(actualEndPoint.X, start.Y, start.Z);
Line line = new Line(start, end);
line.ColorIndex = 1;
if (line.Length < 0.001) continue;
if (!IsOverlapping(line))
{
_lines.Add(line);
draw.Geometry.Draw(line);
}
}
}
// 绘制纵向线
private void DrawVerticalLines(int signX, int signY, Point3d actualEndPoint, WorldDraw draw)
{
if (_xSegments.Count == 0) return;
for (int i = 0; i < _xSegments.Count; i++)
{
double xPos = _xSegments[i];
Point3d start = _startPoint + new Vector3d(signX * xPos, 0, 0);
// 终点自动修剪到实际终点
Point3d end = new Point3d(start.X, actualEndPoint.Y, start.Z);
Line line = new Line(start, end);
line.ColorIndex = 1;
if (line.Length < 0.001) continue;
if (!IsOverlapping(line))
{
_lines.Add(line);
draw.Geometry.Draw(line);
}
}
}
// 重叠检测方法
private bool IsOverlapping(Line newLine)
{
const double toleranceValue = 0.1;
Tolerance tolerance = new Tolerance(toleranceValue, toleranceValue);
foreach (Line existingLine in _lines)
{
bool sameStart = newLine.StartPoint.IsEqualTo(existingLine.StartPoint, tolerance) ||
newLine.StartPoint.IsEqualTo(existingLine.EndPoint, tolerance);
bool sameEnd = newLine.EndPoint.IsEqualTo(existingLine.StartPoint, tolerance) ||
newLine.EndPoint.IsEqualTo(existingLine.EndPoint, tolerance);
if (sameStart && sameEnd)
{
return true;
}
}
return false;
}
// 提交网格到图纸
public void CommitGrid(string layerName)
{
using (Transaction tr = _doc.Database.TransactionManager.StartTransaction())
{
try
{
BlockTable bt = tr.GetObject(_doc.Database.BlockTableId, OpenMode.ForRead) as BlockTable;
BlockTableRecord btr = tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
// 创建图层
LayerTable lt = tr.GetObject(_doc.Database.LayerTableId, OpenMode.ForRead) as LayerTable;
if (!lt.Has(layerName))
{
lt.UpgradeOpen();
LayerTableRecord layer = new LayerTableRecord
{
Name = layerName,
Color = Autodesk.AutoCAD.Colors.Color.FromColorIndex(
Autodesk.AutoCAD.Colors.ColorMethod.ByAci, 1)
};
lt.Add(layer);
tr.AddNewlyCreatedDBObject(layer, true);
}
// 添加所有线段(最终修剪)
foreach (Line line in _lines)
{
if (!line.IsDisposed)
{
// 精确修剪到网格边界
TrimToGridBoundary(line);
// 跳过小于300mm的线段
if (line.Length < 300) continue;
line.Layer = layerName;
line.ColorIndex = 256; // ByLayer
btr.AppendEntity(line);
tr.AddNewlyCreatedDBObject(line, true);
}
}
tr.Commit();
}
catch (SysException)
{
tr.Abort();
throw;
}
}
}
// 精确修剪线段到网格边界
private void TrimToGridBoundary(Line line)
{
// 计算实际网格边界
double maxX = _xSegments.Last();
double maxY = _ySegments.Last();
// 获取线段的实际方向
Vector3d direction = line.EndPoint - line.StartPoint;
// 横向线修剪到最大Y位置
if (Math.Abs(direction.X) > Math.Abs(direction.Y))
{
double currentY = Math.Abs(line.EndPoint.Y - _startPoint.Y);
if (currentY > maxY)
{
line.EndPoint = new Point3d(
line.EndPoint.X,
_startPoint.Y + Math.Sign(direction.Y) * maxY,
line.EndPoint.Z);
}
}
// 纵向线修剪到最大X位置
else
{
double currentX = Math.Abs(line.EndPoint.X - _startPoint.X);
if (currentX > maxX)
{
line.EndPoint = new Point3d(
_startPoint.X + Math.Sign(direction.X) * maxX,
line.EndPoint.Y,
line.EndPoint.Z);
}
}
}
}
}
}
将“CO”功能改为框选需要检查重叠的线段后对重叠段的线段进行合并成一根线,只针对指定的“盘扣轴网”作"CO”功能的处理