沿路径阵列工具
求助,万能的网友以下是一段沿路径阵列的代码,程序总是出错,我的需求是:
1.修正代码(平台为AutoCAD 2014)
2.添加一个界面窗口,类似于3dmax中的间隔工具。
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using System;
namespace AutoCADCommands
{
public class PathArrayCommand
{
// 1. 定义常驻程序的阵列参数变量
private static string arrayMethod = "Measure"; // 阵列方法,默认为测量方式
private static double arraySpacing = 5.0; // 阵列间距,默认为5.0
private static int arrayCount = 5; // 阵列数量,默认为5
private static double pathStartOffset = 0.0; // 始端偏移距离,默认为0.0
private static double pathEndOffset = 0.0; // 末端偏移距离,默认为0.0
private static bool rotateWithPath = true; // 是否旋转至切线方向,默认为true
public static void PathArray()
{
var doc = Application.DocumentManager.MdiActiveDocument;
var db = doc.Database;
var ed = doc.Editor;
try
{
// 2. 选择要阵列的对象
PromptSelectionOptions selOptions = new PromptSelectionOptions();
selOptions.MessageForAdding = "\n选择要阵列的对象: ";
PromptSelectionResult selectionResult = ed.GetSelection(selOptions);
if (selectionResult.Status != PromptStatus.OK)
{
ed.WriteMessage("\n未选择对象,命令取消。");
return; // 未选择对象,退出程序
}
// 计算选择对象的中心点(只考虑二维平面)
Point3d selectionCenter = Point3d.Origin;
bool hasValidEntity = false;
double minX = 0, minY = 0, maxX = 0, maxY = 0;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
try
{
ObjectId[] objectIds = selectionResult.Value.GetObjectIds();
// 遍历所有选中的对象,计算二维边界框
for (int i = 0; i < objectIds.Length; i++)
{
try
{
Entity entity = tr.GetObject(objectIds, OpenMode.ForRead) as Entity;
if (entity != null && entity.Bounds.HasValue)
{
Extents3d extents = entity.Bounds.Value;
// 只考虑二维坐标
double entityMinX = extents.MinPoint.X;
double entityMinY = extents.MinPoint.Y;
double entityMaxX = extents.MaxPoint.X;
double entityMaxY = extents.MaxPoint.Y;
if (!hasValidEntity)
{
// 第一个有效对象,初始化边界值
minX = entityMinX;
minY = entityMinY;
maxX = entityMaxX;
maxY = entityMaxY;
hasValidEntity = true;
}
else
{
// 更新最小值和最大值
if (entityMinX < minX) minX = entityMinX;
if (entityMinY < minY) minY = entityMinY;
if (entityMaxX > maxX) maxX = entityMaxX;
if (entityMaxY > maxY) maxY = entityMaxY;
}
}
}
catch
{
// 忽略无法处理的实体
continue;
}
}
}
catch (Autodesk.AutoCAD.Runtime.Exception ex)
{
ed.WriteMessage("\n计算中心点时出错:" + ex.Message);
selectionCenter = new Point3d(0, 0, 0);
}
tr.Commit();
}
if (hasValidEntity)
{
// 计算二维几何中心点,Z坐标为0
double centerX = (minX + maxX) / 2.0;
double centerY = (minY + maxY) / 2.0;
selectionCenter = new Point3d(centerX, centerY, 0.0);
}
else
{
ed.WriteMessage("\n未找到有效对象或对象没有边界框。");
return;
}
// 3. 指定阵列基点<中心点>
PromptPointOptions basePointOptions = new PromptPointOptions("\n指定阵列基点,或<中心点(Enter)>: ");
basePointOptions.AllowNone = true;
basePointOptions.UseBasePoint = true;
basePointOptions.BasePoint = selectionCenter;
PromptPointResult basePointResult = ed.GetPoint(basePointOptions);
Point3d basePoint;
if (basePointResult.Status == PromptStatus.Cancel)
{
ed.WriteMessage("\n命令已取消。");
return;
}
else if (basePointResult.Status == PromptStatus.None)
{
// 用户按了Enter,使用计算出的中心点
basePoint = selectionCenter;
}
else if (basePointResult.Status == PromptStatus.OK)
{
// 用户指定了点,使用该点
basePoint = basePointResult.Value;
// 确保Z坐标为0(二维平面)
basePoint = new Point3d(basePoint.X, basePoint.Y, 0.0);
}
else
{
// 其他状态,使用默认中心点
basePoint = selectionCenter;
}
// 4. 显示当前阵列参数并提示用户修改
bool continueEditingParams = true;
while (continueEditingParams)
{
try
{
// 显示当前参数
string offsetInfo = "始端偏移<" + pathStartOffset.ToString("F2") + ">,末端偏移<" + pathEndOffset.ToString("F2") + ">,旋转< " + (rotateWithPath ? "是" : "否") + ">";
if (arrayMethod == "Measure")
{
ed.WriteMessage("\n当前阵列参数:按间距<" + arraySpacing.ToString("F2") + ">," + offsetInfo);
}
else
{
ed.WriteMessage("\n当前阵列参数:按数量<" + arrayCount.ToString() + ">," + offsetInfo);
}
// 提示用户选择参数选项
PromptKeywordOptions paramOptions = new PromptKeywordOptions("\n选择阵列参数[间距(Distance)/数量(Count)/始端偏移(Start)/末端偏移(End)/旋转(Rotate)]<确定(Enter)>: ");
paramOptions.Keywords.Add("Distance");
paramOptions.Keywords.Add("Count");
paramOptions.Keywords.Add("Start");
paramOptions.Keywords.Add("End");
paramOptions.Keywords.Add("Rotate");
paramOptions.AllowNone = true;//按 ENTER 键(NULL 输入)时使用默认值
paramOptions.Keywords.Default = "None";
PromptResult paramResult = ed.GetKeywords(paramOptions);
if (paramResult.Status == PromptStatus.OK)
{
switch (paramResult.StringResult)
{
case "Distance": // 设置间距
PromptDistanceOptions spacingOptions = new PromptDistanceOptions("\n指定间距 <" + arraySpacing.ToString("F2") + ">: ");
spacingOptions.DefaultValue = arraySpacing;
spacingOptions.AllowNegative = false;
spacingOptions.AllowZero = false;
spacingOptions.UseBasePoint = true;
spacingOptions.BasePoint = basePoint;
PromptDoubleResult spacingResult = ed.GetDistance(spacingOptions);
if (spacingResult.Status == PromptStatus.OK)
{
arraySpacing = spacingResult.Value;
arrayMethod = "Measure"; // 切换到测量方式
}
break;
case "Count": // 设置数量
PromptIntegerOptions countOptions = new PromptIntegerOptions("\n输入数量 <" + arrayCount + ">: ");
countOptions.DefaultValue = arrayCount;
countOptions.LowerLimit = 2;
PromptIntegerResult countResult = ed.GetInteger(countOptions);
if (countResult.Status == PromptStatus.OK)
{
arrayCount = countResult.Value;
arrayMethod = "Count"; // 切换到等分方式
}
break;
case "Start": // 设置始端偏移
PromptDistanceOptions startOffsetOptions = new PromptDistanceOptions("\n指定始端偏移距离 <" + pathStartOffset.ToString("F2") + ">: ");
startOffsetOptions.DefaultValue = pathStartOffset;
startOffsetOptions.AllowNegative = false; // 不允许负值,只能从起点向后偏移
PromptDoubleResult startOffsetResult = ed.GetDistance(startOffsetOptions);
if (startOffsetResult.Status == PromptStatus.OK)
{
pathStartOffset = startOffsetResult.Value;
}
break;
case "End": // 设置末端偏移
PromptDistanceOptions endOffsetOptions = new PromptDistanceOptions("\n指定末端偏移距离 <" + pathEndOffset.ToString("F2") + ">: ");
endOffsetOptions.DefaultValue = pathEndOffset;
endOffsetOptions.AllowNegative = false; // 不允许负值,只能从终点向前偏移
PromptDoubleResult endOffsetResult = ed.GetDistance(endOffsetOptions);
if (endOffsetResult.Status == PromptStatus.OK)
{
pathEndOffset = endOffsetResult.Value;
}
break;
case "Rotate": // 切换旋转选项
rotateWithPath = !rotateWithPath;
ed.WriteMessage("\n路径旋转已" + (rotateWithPath ? "开启" : "关闭"));
break;
}
}
else if (paramResult.Status == PromptStatus.None)
{
continueEditingParams = false; // 用户按回车,结束参数设置
}
else
{
ed.WriteMessage("\n用户取消,退出程序。");
return;
}
}
catch (Autodesk.AutoCAD.Runtime.Exception ex)
{
ed.WriteMessage("\n参数设置时出错:" + ex.Message);
continueEditingParams = false;
}
}
// 5. 开始路径阵列循环
bool continueArray = true;
while (continueArray)
{
try
{
// 点选一条路径
PromptEntityOptions pathOptions = new PromptEntityOptions("\n选择路径: ");
pathOptions.SetRejectMessage("\n所选对象不是有效路径。");
pathOptions.AddAllowedClass(typeof(Curve), false);
PromptEntityResult pathResult = ed.GetEntity(pathOptions);
if (pathResult.Status == PromptStatus.Cancel)
{
ed.WriteMessage("\n命令取消。");
break;
}
else if (pathResult.Status != PromptStatus.OK)
{
break; // 未选择路径,结束循环
}
// 检查是否为有效路径(排除构造线和射线)
using (Transaction tr = db.TransactionManager.StartOpenCloseTransaction())
{
DBObject obj = tr.GetObject(pathResult.ObjectId, OpenMode.ForRead);
string dxfName = obj.GetType().Name;
if (dxfName == "Xline" || dxfName == "Ray")
{
ed.WriteMessage("\n构造线和射线不能作为路径。");
continue;
}
}
// 沿该路径创建对象阵列
if (!CreatePathArray(db, ed, basePoint, selectionResult.Value.GetObjectIds(),pathResult.ObjectId))
{
ed.WriteMessage("\n阵列创建失败。");
}
// 询问是否继续
PromptKeywordOptions continueOptions = new PromptKeywordOptions("\n是否继续阵列? [是(Y)/否(N)] <是>: ");
continueOptions.Keywords.Add("Y");
continueOptions.Keywords.Add("N");
continueOptions.AllowNone = true;
continueOptions.Keywords.Default = "Y";
PromptResult continueResult = ed.GetKeywords(continueOptions);
if (continueResult.Status == PromptStatus.Cancel)
{
ed.WriteMessage("\n命令取消。");
continueArray = false;
}
else if (continueResult.Status != PromptStatus.OK ||
(continueResult.StringResult == "N" ||
(continueResult.Status == PromptStatus.None && continueResult.StringResult != "Y")))
{
continueArray = false;
}
}
catch (Autodesk.AutoCAD.Runtime.Exception ex)
{
ed.WriteMessage("\n阵列过程中出错: " +ex.Message);
continueArray = false;
}
}
ed.WriteMessage("\n路径阵列完成。");
}
catch (System.Runtime.InteropServices.SEHException sehEx)
{
ed.WriteMessage("\n系统异常: 请确保选择的对象类型正确。");
Application.DocumentManager.MdiActiveDocument.SendStringToExecute("(princ)\n", true, false, false);
}
catch (Autodesk.AutoCAD.Runtime.Exception ex)
{
ed.WriteMessage("\n命令执行时发生错误: " +ex.Message);
Application.DocumentManager.MdiActiveDocument.SendStringToExecute("(princ)\n", true, false, false);
}
}
// 创建路径阵列的辅助方法(二维平面)
private static bool CreatePathArray(Database db, Editor ed, Point3d basePoint,
ObjectId[] sourceIds, ObjectId pathId)
{
using (Transaction tr = db.TransactionManager.StartTransaction())
{
try
{
// 获取路径曲线
Curve path = tr.GetObject(pathId, OpenMode.ForRead) as Curve;
if (path == null)
{
ed.WriteMessage("\n无法获取路径对象。");
return false;
}
// 获取路径长度(二维平面)
double totalLength = 0;
try
{
totalLength = path.GetDistanceAtParameter(path.EndParam);
}
catch
{
// 对于某些类型的曲线,使用替代方法
try
{
//totalLength = path.GetDistanceAtPoint(path.EndPoint);
totalLength = path.GetDistAtPoint(path.EndPoint);
}
catch
{
ed.WriteMessage("\n无法计算路径长度。");
return false;
}
}
// 应用始端和末端偏移
double effectiveLength = totalLength - pathStartOffset - pathEndOffset;
if (effectiveLength <= 0)
{
ed.WriteMessage("\n路径长度不足或偏移量过大。");
return false;
}
// 根据阵列方法计算元素位置
double[] positions;
if (arrayMethod == "Count") // 等分方式
{
if (arrayCount < 2)
{
ed.WriteMessage("\n阵列数量必须大于1。");
return false;
}
positions = new double;
double interval = effectiveLength / (arrayCount - 1);
for (int i = 0; i < arrayCount; i++)
{
positions = pathStartOffset + i * interval;
}
}
else // 测量方式
{
if (arraySpacing <= 0)
{
ed.WriteMessage("\n阵列间距必须大于0。");
return false;
}
int count = (int)(effectiveLength / arraySpacing) + 1;
if (count < 2) count = 2;
positions = new double;
for (int i = 0; i < count; i++)
{
positions = pathStartOffset + i * arraySpacing;
}
}
// 获取路径起点处的切线方向(用于旋转参考)
double startRotation = 0;
if (rotateWithPath)
{
try
{
double startParam = path.GetParameterAtDistance(pathStartOffset);
Vector3d startTangent = path.GetFirstDerivative(startParam).GetNormal();
// 二维平面旋转,只考虑XY平面
startRotation = Math.Atan2(startTangent.Y, startTangent.X);
}
catch
{
// 如果无法获取切线方向,使用默认值
startRotation = 0;
}
}
// 创建源对象的ID集合
ObjectIdCollection sourceIdCollection = new ObjectIdCollection(sourceIds);
// 沿路径创建每个阵列副本
for (int i = 0; i < positions.Length; i++)
{
double distance = positions;
// 确保距离在路径范围内
if (distance < 0 || distance > totalLength)
continue;
// 获取路径上的点
Point3d pointOnPath;
try
{
pointOnPath = path.GetPointAtDist(distance);
// 确保点在二维平面
pointOnPath = new Point3d(pointOnPath.X, pointOnPath.Y, 0.0);
}
catch
{
// 如果距离超出范围,跳过
continue;
}
// 克隆源对象
IdMapping mapping = new IdMapping();
db.DeepCloneObjects(sourceIdCollection, db.CurrentSpaceId, mapping, false);
// 对克隆的对象应用变换
foreach (IdPair pair in mapping)
{
if (pair.IsCloned && pair.IsPrimary)
{
Entity clonedEntity = tr.GetObject(pair.Value, OpenMode.ForWrite) as Entity;
if (clonedEntity != null)
{
// 1. 从基点平移到路径点(二维平移)
Vector3d translation = basePoint.GetVectorTo(pointOnPath);
Matrix3d transform = Matrix3d.Displacement(translation);
// 2. 如果需要,旋转至切线方向(二维旋转)
if (rotateWithPath)
{
try
{
double currentParam = path.GetParameterAtDistance(distance);
Vector3d currentTangent = path.GetFirstDerivative(currentParam).GetNormal();
// 二维平面旋转角度
double currentRotation = Math.Atan2(currentTangent.Y, currentTangent.X);
double rotationAngle = currentRotation - startRotation;
// 以路径点为中心旋转(Z轴旋转)
if (Math.Abs(rotationAngle) > 1e-10) // 避免微小旋转
{
transform = transform *
Matrix3d.Rotation(rotationAngle, Vector3d.ZAxis, pointOnPath);
}
}
catch
{
// 如果无法计算旋转,继续执行平移
}
}
// 应用变换
clonedEntity.TransformBy(transform);
}
}
}
}
tr.Commit();
return true;
}
catch (System.Runtime.InteropServices.SEHException sehEx)
{
ed.WriteMessage("\n系统异常: 路径操作失败。");
return false;
}
catch (Autodesk.AutoCAD.Runtime.Exception ex)
{
ed.WriteMessage("\n创建阵列时出错:"+ex.Message);
return false;
}
}
}
}
}
我先来,AI生成的就让AI去完善 :lol tranque 发表于 2026-1-4 12:23
我先来,AI生成的就让AI去完善
人家都没有说时ai生产的啊
qifeifei 发表于 2026-1-4 14:09
人家都没有说时ai生产的啊
感觉就很像:lol 代码质量越来越差了,连tr.GetObject(id)都不放using了...
而且开了n个事务,还混开了 StartOpenCloseTransaction...
不知道怎么评价 楼主 这个代码怎么加载到CAD里没接触过求教 本帖最后由 你有种再说一遍 于 2026-1-4 17:59 编辑
小鸟 发表于 2026-1-4 17:55
楼主 这个代码怎么加载到CAD里没接触过求教
去b站搜cad c#二次开发,需要下载vs(不是vs code),编译为dll. 你有种再说一遍 发表于 2026-1-4 17:53
代码质量越来越差了,连tr.GetObject(id)都不放using了...
而且开了n个事务,还混开了 StartOpenCloseTransa ...
你说的是 这个么?
using (Transaction tr = db.TransactionManager.StartTransaction())
{
try
{
tr.Commit();
}
} 还要带界面,那就要耗费一些时间了,不是一个简单评论就能回答的完的,不如发到编程申请或者付费开发区找人帮你重写一个 77077 发表于 2026-1-5 09:13
你说的是 这个么?
using (Transaction tr = db.TransactionManager.StartTransaction())
{
去看看这个项目吧,test文件夹下面都是案例,
写了大量的命令了.
https://gitee.com/inspirefunction/ifoxcad
页:
[1]
2