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, btr);
// 处理垂直线重叠
ProcessOverlappingLines(verticalLines, false, tr, btr);
tr.Commit();
ed.WriteMessage("\n重叠处理完成!");
}
}
catch (SysException ex)
{
ed.WriteMessage($"\n错误: {ex.Message}");
}
}
// 处理同一方向上的线段重叠
private void ProcessOverlappingLines(List<Line> lines, bool isHorizontal, Transaction tr, BlockTableRecord btr)
{
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)
);
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();
bool modified;
do
{
modified = false;
for (int i = 0; i < sortedLines.Count - 1; i++)
{
Line line1 = sortedLines[i];
Line line2 = sortedLines[i + 1];
// 获取线段范围
double start1, end1, start2, end2;
GetLineRange(line1, isHorizontal, out start1, out end1);
GetLineRange(line2, isHorizontal, out start2, out end2);
// 检查重叠
if (end1 > start2 && end2 > start1)
{
double overlapStart = Math.Max(start1, start2);
double overlapEnd = Math.Min(end1, end2);
// 分割线段并删除重叠部分
List<Line> newParts1 = SplitLine(line1, overlapStart, overlapEnd, isHorizontal, tr, btr);
List<Line> newParts2 = SplitLine(line2, overlapStart, overlapEnd, isHorizontal, tr, btr);
// 移除原线段
line1.UpgradeOpen();
line1.Erase();
line2.UpgradeOpen();
line2.Erase();
// 添加新分段
sortedLines.RemoveRange(i, 2);
sortedLines.InsertRange(i, newParts1);
sortedLines.InsertRange(i + newParts1.Count, newParts2);
modified = true;
break; // 重新开始检查
}
}
} while (modified); // 重复直到无重叠
}
}
// 获取线段在指定方向的范围
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 List<Line> SplitLine(Line line, double overlapStart, double overlapEnd,
bool isHorizontal, Transaction tr, BlockTableRecord btr)
{
List<Line> newLines = new List<Line>();
double lineStart, lineEnd;
GetLineRange(line, isHorizontal, out lineStart, out lineEnd);
// 前段(重叠前)
if (lineStart < overlapStart - 0.001) // 避免浮点误差
{
Line frontPart = CreateLineSegment(line, lineStart, overlapStart, isHorizontal);
btr.AppendEntity(frontPart);
tr.AddNewlyCreatedDBObject(frontPart, true);
newLines.Add(frontPart);
}
// 后段(重叠后)
if (lineEnd > overlapEnd + 0.001)
{
Line backPart = CreateLineSegment(line, overlapEnd, lineEnd, isHorizontal);
btr.AppendEntity(backPart);
tr.AddNewlyCreatedDBObject(backPart, true);
newLines.Add(backPart);
}
return newLines;
}
// 创建线段分段
private Line CreateLineSegment(Line original, double startVal, double endVal, bool isHorizontal)
{
Point3d startPt, endPt;
if (isHorizontal)
{
startPt = new Point3d(startVal, original.StartPoint.Y, 0);
endPt = new Point3d(endVal, original.StartPoint.Y, 0);
}
else
{
startPt = new Point3d(original.StartPoint.X, startVal, 0);
endPt = new Point3d(original.StartPoint.X, endVal, 0);
}
Line newLine = new Line(startPt, endPt)
{
Layer = original.Layer,
ColorIndex = original.ColorIndex,
Linetype = original.Linetype,
LinetypeScale = original.LinetypeScale
};
return newLine;
}
// 检查两条线是否重叠
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)
{
double remaining = totalLength - currentPos;
// 尝试使用固定值
double segmentLength = FixedLengths
.Where(len => len <= remaining)
.DefaultIfEmpty(0)
.Max();
// 如果没有合适固定值,尝试使用起始间距
if (segmentLength < 300 && spacing <= remaining)
{
segmentLength = spacing;
}
// 如果仍然不满足条件,跳过此段
if (segmentLength < 300)
{
break;
}
currentPos += segmentLength;
// 确保不超过总长度
if (currentPos > totalLength)
{
currentPos = totalLength;
}
segments.Add(currentPos);
segmentCount++;
if (currentPos >= totalLength) 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);
}
}
}
}
}
}
CAD运行中存在两个问题
1、DG功能布线的时候未按指定横向和纵向输入的值进行连续布置
2、CO功能修剪重叠线后,删掉重叠线段至少要保留一根,