求助,万能的网友
以下是一段沿路径阵列的代码,程序总是出错,我的需求是:
1.修正代码(平台为AutoCAD 2014)
2.添加一个界面窗口,类似于3dmax中的间隔工具。


  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Geometry;
  5. using Autodesk.AutoCAD.Runtime;
  6. using System;

  7. namespace AutoCADCommands
  8. {
  9.     public class PathArrayCommand
  10.     {
  11.         // 1. 定义常驻程序的阵列参数变量
  12.         private static string arrayMethod = "Measure";        // 阵列方法,默认为测量方式
  13.         private static double arraySpacing = 5.0;            // 阵列间距,默认为5.0
  14.         private static int arrayCount = 5;                   // 阵列数量,默认为5
  15.         private static double pathStartOffset = 0.0;         // 始端偏移距离,默认为0.0
  16.         private static double pathEndOffset = 0.0;           // 末端偏移距离,默认为0.0
  17.         private static bool rotateWithPath = true;           // 是否旋转至切线方向,默认为true

  18.         [CommandMethod("PATHARRAY")]
  19.         public static void PathArray()
  20.         {
  21.             var doc = Application.DocumentManager.MdiActiveDocument;
  22.             var db = doc.Database;
  23.             var ed = doc.Editor;

  24.             try
  25.             {
  26.                 // 2. 选择要阵列的对象
  27.                 PromptSelectionOptions selOptions = new PromptSelectionOptions();
  28.                 selOptions.MessageForAdding = "\n选择要阵列的对象: ";
  29.                 PromptSelectionResult selectionResult = ed.GetSelection(selOptions);
  30.                 if (selectionResult.Status != PromptStatus.OK)
  31.                 {
  32.                     ed.WriteMessage("\n未选择对象,命令取消。");
  33.                     return; // 未选择对象,退出程序
  34.                 }

  35.                 // 计算选择对象的中心点(只考虑二维平面)
  36.                 Point3d selectionCenter = Point3d.Origin;
  37.                 bool hasValidEntity = false;
  38.                 double minX = 0, minY = 0, maxX = 0, maxY = 0;

  39.                 using (Transaction tr = db.TransactionManager.StartTransaction())
  40.                 {
  41.                     try
  42.                     {
  43.                         ObjectId[] objectIds = selectionResult.Value.GetObjectIds();
  44.                         
  45.                         // 遍历所有选中的对象,计算二维边界框
  46.                         for (int i = 0; i < objectIds.Length; i++)
  47.                         {
  48.                             try
  49.                             {
  50.                                 Entity entity = tr.GetObject(objectIds, OpenMode.ForRead) as Entity;
  51.                                 if (entity != null && entity.Bounds.HasValue)
  52.                                 {
  53.                                     Extents3d extents = entity.Bounds.Value;
  54.                                     
  55.                                     // 只考虑二维坐标
  56.                                     double entityMinX = extents.MinPoint.X;
  57.                                     double entityMinY = extents.MinPoint.Y;
  58.                                     double entityMaxX = extents.MaxPoint.X;
  59.                                     double entityMaxY = extents.MaxPoint.Y;
  60.                                     
  61.                                     if (!hasValidEntity)
  62.                                     {
  63.                                         // 第一个有效对象,初始化边界值
  64.                                         minX = entityMinX;
  65.                                         minY = entityMinY;
  66.                                         maxX = entityMaxX;
  67.                                         maxY = entityMaxY;
  68.                                         hasValidEntity = true;
  69.                                     }
  70.                                     else
  71.                                     {
  72.                                         // 更新最小值和最大值
  73.                                         if (entityMinX < minX) minX = entityMinX;
  74.                                         if (entityMinY < minY) minY = entityMinY;
  75.                                         if (entityMaxX > maxX) maxX = entityMaxX;
  76.                                         if (entityMaxY > maxY) maxY = entityMaxY;
  77.                                     }
  78.                                 }
  79.                             }
  80.                             catch
  81.                             {
  82.                                 // 忽略无法处理的实体
  83.                                 continue;
  84.                             }
  85.                         }
  86.                     }
  87.                     catch (Autodesk.AutoCAD.Runtime.Exception ex)
  88.                     {
  89.                         ed.WriteMessage("\n计算中心点时出错:" + ex.Message);
  90.                         selectionCenter = new Point3d(0, 0, 0);
  91.                     }
  92.                     tr.Commit();
  93.                 }

  94.                 if (hasValidEntity)
  95.                 {
  96.                     // 计算二维几何中心点,Z坐标为0
  97.                     double centerX = (minX + maxX) / 2.0;
  98.                     double centerY = (minY + maxY) / 2.0;
  99.                     selectionCenter = new Point3d(centerX, centerY, 0.0);
  100.                 }
  101.                 else
  102.                 {
  103.                     ed.WriteMessage("\n未找到有效对象或对象没有边界框。");
  104.                     return;
  105.                 }

  106.                 // 3. 指定阵列基点<中心点>
  107.                 PromptPointOptions basePointOptions = new PromptPointOptions("\n指定阵列基点,或<中心点(Enter)>: ");
  108.                 basePointOptions.AllowNone = true;
  109.                 basePointOptions.UseBasePoint = true;
  110.                 basePointOptions.BasePoint = selectionCenter;

  111.                 PromptPointResult basePointResult = ed.GetPoint(basePointOptions);
  112.                 Point3d basePoint;
  113.                 if (basePointResult.Status == PromptStatus.Cancel)
  114.                 {
  115.                     ed.WriteMessage("\n命令已取消。");
  116.                     return;
  117.                 }
  118.                 else if (basePointResult.Status == PromptStatus.None)
  119.                 {
  120.                     // 用户按了Enter,使用计算出的中心点
  121.                     basePoint = selectionCenter;
  122.                 }
  123.                 else if (basePointResult.Status == PromptStatus.OK)
  124.                 {
  125.                     // 用户指定了点,使用该点
  126.                     basePoint = basePointResult.Value;
  127.                     // 确保Z坐标为0(二维平面)
  128.                     basePoint = new Point3d(basePoint.X, basePoint.Y, 0.0);
  129.                 }
  130.                 else
  131.                 {
  132.                     // 其他状态,使用默认中心点
  133.                     basePoint = selectionCenter;
  134.                 }

  135.                 // 4. 显示当前阵列参数并提示用户修改
  136.                 bool continueEditingParams = true;
  137.                 while (continueEditingParams)
  138.                 {
  139.                     try
  140.                     {
  141.                         // 显示当前参数
  142.                         string offsetInfo = "始端偏移<" + pathStartOffset.ToString("F2") + ">,末端偏移<" + pathEndOffset.ToString("F2") + ">,旋转< " + (rotateWithPath ? "是" : "否") + ">";
  143.                         if (arrayMethod == "Measure")
  144.                         {
  145.                             ed.WriteMessage("\n当前阵列参数:按间距<" + arraySpacing.ToString("F2") + ">," + offsetInfo);
  146.                         }
  147.                         else
  148.                         {
  149.                             ed.WriteMessage("\n当前阵列参数:按数量<" + arrayCount.ToString() + ">," + offsetInfo);
  150.                         }

  151.                         // 提示用户选择参数选项
  152.                         PromptKeywordOptions paramOptions = new PromptKeywordOptions("\n选择阵列参数[间距(Distance)/数量(Count)/始端偏移(Start)/末端偏移(End)/旋转(Rotate)]<确定(Enter)>: ");
  153.                         paramOptions.Keywords.Add("Distance");
  154.                         paramOptions.Keywords.Add("Count");
  155.                         paramOptions.Keywords.Add("Start");
  156.                         paramOptions.Keywords.Add("End");
  157.                         paramOptions.Keywords.Add("Rotate");
  158.                         paramOptions.AllowNone = true;  //按 ENTER 键(NULL 输入)时使用默认值
  159.                         paramOptions.Keywords.Default = "None";

  160.                         PromptResult paramResult = ed.GetKeywords(paramOptions);

  161.                         if (paramResult.Status == PromptStatus.OK)
  162.                         {
  163.                             switch (paramResult.StringResult)
  164.                             {
  165.                                 case "Distance": // 设置间距
  166.                                     PromptDistanceOptions spacingOptions = new PromptDistanceOptions("\n指定间距 <" + arraySpacing.ToString("F2") + ">: ");
  167.                                     spacingOptions.DefaultValue = arraySpacing;
  168.                                     spacingOptions.AllowNegative = false;
  169.                                     spacingOptions.AllowZero = false;
  170.                                     spacingOptions.UseBasePoint = true;
  171.                                     spacingOptions.BasePoint = basePoint;

  172.                                     PromptDoubleResult spacingResult = ed.GetDistance(spacingOptions);
  173.                                     if (spacingResult.Status == PromptStatus.OK)
  174.                                     {
  175.                                         arraySpacing = spacingResult.Value;
  176.                                         arrayMethod = "Measure"; // 切换到测量方式
  177.                                     }
  178.                                     break;

  179.                                 case "Count": // 设置数量
  180.                                     PromptIntegerOptions countOptions = new PromptIntegerOptions("\n输入数量 <" + arrayCount + ">: ");
  181.                                     countOptions.DefaultValue = arrayCount;
  182.                                     countOptions.LowerLimit = 2;

  183.                                     PromptIntegerResult countResult = ed.GetInteger(countOptions);
  184.                                     if (countResult.Status == PromptStatus.OK)
  185.                                     {
  186.                                         arrayCount = countResult.Value;
  187.                                         arrayMethod = "Count"; // 切换到等分方式
  188.                                     }
  189.                                     break;

  190.                                 case "Start": // 设置始端偏移
  191.                                     PromptDistanceOptions startOffsetOptions = new PromptDistanceOptions("\n指定始端偏移距离 <" + pathStartOffset.ToString("F2") + ">: ");
  192.                                     startOffsetOptions.DefaultValue = pathStartOffset;
  193.                                     startOffsetOptions.AllowNegative = false; // 不允许负值,只能从起点向后偏移

  194.                                     PromptDoubleResult startOffsetResult = ed.GetDistance(startOffsetOptions);
  195.                                     if (startOffsetResult.Status == PromptStatus.OK)
  196.                                     {
  197.                                         pathStartOffset = startOffsetResult.Value;
  198.                                     }
  199.                                     break;

  200.                                 case "End": // 设置末端偏移
  201.                                     PromptDistanceOptions endOffsetOptions = new PromptDistanceOptions("\n指定末端偏移距离 <" + pathEndOffset.ToString("F2") + ">: ");
  202.                                     endOffsetOptions.DefaultValue = pathEndOffset;
  203.                                     endOffsetOptions.AllowNegative = false; // 不允许负值,只能从终点向前偏移

  204.                                     PromptDoubleResult endOffsetResult = ed.GetDistance(endOffsetOptions);
  205.                                     if (endOffsetResult.Status == PromptStatus.OK)
  206.                                     {
  207.                                         pathEndOffset = endOffsetResult.Value;
  208.                                     }
  209.                                     break;

  210.                                 case "Rotate": // 切换旋转选项
  211.                                     rotateWithPath = !rotateWithPath;
  212.                                     ed.WriteMessage("\n路径旋转已" + (rotateWithPath ? "开启" : "关闭"));
  213.                                     break;
  214.                             }
  215.                         }
  216.                         else if (paramResult.Status == PromptStatus.None)
  217.                         {
  218.                             continueEditingParams = false; // 用户按回车,结束参数设置
  219.                         }
  220.                         else
  221.                         {
  222.                             ed.WriteMessage("\n用户取消,退出程序。");
  223.                             return;
  224.                         }
  225.                     }
  226.                     catch (Autodesk.AutoCAD.Runtime.Exception ex)
  227.                     {
  228.                         ed.WriteMessage("\n参数设置时出错:" + ex.Message);
  229.                         continueEditingParams = false;
  230.                     }
  231.                 }

  232.                 // 5. 开始路径阵列循环
  233.                 bool continueArray = true;
  234.                 while (continueArray)
  235.                 {
  236.                     try
  237.                     {
  238.                         // 点选一条路径
  239.                         PromptEntityOptions pathOptions = new PromptEntityOptions("\n选择路径: ");
  240.                         pathOptions.SetRejectMessage("\n所选对象不是有效路径。");
  241.                         pathOptions.AddAllowedClass(typeof(Curve), false);

  242.                         PromptEntityResult pathResult = ed.GetEntity(pathOptions);
  243.                         if (pathResult.Status == PromptStatus.Cancel)
  244.                         {
  245.                             ed.WriteMessage("\n命令取消。");
  246.                             break;
  247.                         }
  248.                         else if (pathResult.Status != PromptStatus.OK)
  249.                         {
  250.                             break; // 未选择路径,结束循环
  251.                         }

  252.                         // 检查是否为有效路径(排除构造线和射线)
  253.                         using (Transaction tr = db.TransactionManager.StartOpenCloseTransaction())
  254.                         {
  255.                             DBObject obj = tr.GetObject(pathResult.ObjectId, OpenMode.ForRead);
  256.                             string dxfName = obj.GetType().Name;
  257.                             if (dxfName == "Xline" || dxfName == "Ray")
  258.                             {
  259.                                 ed.WriteMessage("\n构造线和射线不能作为路径。");
  260.                                 continue;
  261.                             }
  262.                         }

  263.                         // 沿该路径创建对象阵列
  264.                         if (!CreatePathArray(db, ed, basePoint, selectionResult.Value.GetObjectIds(),pathResult.ObjectId))
  265.                         {
  266.                             ed.WriteMessage("\n阵列创建失败。");
  267.                         }

  268.                         // 询问是否继续
  269.                         PromptKeywordOptions continueOptions = new PromptKeywordOptions("\n是否继续阵列? [是(Y)/否(N)] <是>: ");
  270.                         continueOptions.Keywords.Add("Y");
  271.                         continueOptions.Keywords.Add("N");
  272.                         continueOptions.AllowNone = true;
  273.                         continueOptions.Keywords.Default = "Y";

  274.                         PromptResult continueResult = ed.GetKeywords(continueOptions);
  275.                         if (continueResult.Status == PromptStatus.Cancel)
  276.                         {
  277.                             ed.WriteMessage("\n命令取消。");
  278.                             continueArray = false;
  279.                         }
  280.                         else if (continueResult.Status != PromptStatus.OK ||
  281.                                 (continueResult.StringResult == "N" ||
  282.                                 (continueResult.Status == PromptStatus.None && continueResult.StringResult != "Y")))
  283.                         {
  284.                             continueArray = false;
  285.                         }
  286.                     }
  287.                     catch (Autodesk.AutoCAD.Runtime.Exception ex)
  288.                     {
  289.                         ed.WriteMessage("\n阵列过程中出错: " +ex.Message);
  290.                         continueArray = false;
  291.                     }
  292.                 }

  293.                 ed.WriteMessage("\n路径阵列完成。");
  294.             }
  295.             catch (System.Runtime.InteropServices.SEHException sehEx)
  296.             {
  297.                 ed.WriteMessage("\n系统异常: 请确保选择的对象类型正确。");
  298.                 Application.DocumentManager.MdiActiveDocument.SendStringToExecute("(princ)\n", true, false, false);
  299.             }
  300.             catch (Autodesk.AutoCAD.Runtime.Exception ex)
  301.             {
  302.                 ed.WriteMessage("\n命令执行时发生错误: " +ex.Message);
  303.                 Application.DocumentManager.MdiActiveDocument.SendStringToExecute("(princ)\n", true, false, false);
  304.             }
  305.         }

  306.         // 创建路径阵列的辅助方法(二维平面)
  307.         private static bool CreatePathArray(Database db, Editor ed, Point3d basePoint,
  308.                                            ObjectId[] sourceIds, ObjectId pathId)
  309.         {
  310.             using (Transaction tr = db.TransactionManager.StartTransaction())
  311.             {
  312.                 try
  313.                 {
  314.                     // 获取路径曲线
  315.                     Curve path = tr.GetObject(pathId, OpenMode.ForRead) as Curve;
  316.                     if (path == null)
  317.                     {
  318.                         ed.WriteMessage("\n无法获取路径对象。");
  319.                         return false;
  320.                     }

  321.                     // 获取路径长度(二维平面)
  322.                     double totalLength = 0;
  323.                     try
  324.                     {
  325.                         totalLength = path.GetDistanceAtParameter(path.EndParam);
  326.                     }
  327.                     catch
  328.                     {
  329.                         // 对于某些类型的曲线,使用替代方法
  330.                         try
  331.                         {
  332.                             //totalLength = path.GetDistanceAtPoint(path.EndPoint);
  333.                             totalLength = path.GetDistAtPoint(path.EndPoint);
  334.                         }
  335.                         catch
  336.                         {
  337.                             ed.WriteMessage("\n无法计算路径长度。");
  338.                             return false;
  339.                         }
  340.                     }

  341.                     // 应用始端和末端偏移
  342.                     double effectiveLength = totalLength - pathStartOffset - pathEndOffset;
  343.                     if (effectiveLength <= 0)
  344.                     {
  345.                         ed.WriteMessage("\n路径长度不足或偏移量过大。");
  346.                         return false;
  347.                     }

  348.                     // 根据阵列方法计算元素位置
  349.                     double[] positions;
  350.                     if (arrayMethod == "Count") // 等分方式
  351.                     {
  352.                         if (arrayCount < 2)
  353.                         {
  354.                             ed.WriteMessage("\n阵列数量必须大于1。");
  355.                             return false;
  356.                         }

  357.                         positions = new double[arrayCount];
  358.                         double interval = effectiveLength / (arrayCount - 1);
  359.                         for (int i = 0; i < arrayCount; i++)
  360.                         {
  361.                             positions = pathStartOffset + i * interval;
  362.                         }
  363.                     }
  364.                     else // 测量方式
  365.                     {
  366.                         if (arraySpacing <= 0)
  367.                         {
  368.                             ed.WriteMessage("\n阵列间距必须大于0。");
  369.                             return false;
  370.                         }

  371.                         int count = (int)(effectiveLength / arraySpacing) + 1;
  372.                         if (count < 2) count = 2;

  373.                         positions = new double[count];
  374.                         for (int i = 0; i < count; i++)
  375.                         {
  376.                             positions = pathStartOffset + i * arraySpacing;
  377.                         }
  378.                     }

  379.                     // 获取路径起点处的切线方向(用于旋转参考)
  380.                     double startRotation = 0;
  381.                     if (rotateWithPath)
  382.                     {
  383.                         try
  384.                         {
  385.                             double startParam = path.GetParameterAtDistance(pathStartOffset);
  386.                             Vector3d startTangent = path.GetFirstDerivative(startParam).GetNormal();
  387.                            
  388.                             // 二维平面旋转,只考虑XY平面
  389.                             startRotation = Math.Atan2(startTangent.Y, startTangent.X);
  390.                         }
  391.                         catch
  392.                         {
  393.                             // 如果无法获取切线方向,使用默认值
  394.                             startRotation = 0;
  395.                         }
  396.                     }

  397.                     // 创建源对象的ID集合
  398.                     ObjectIdCollection sourceIdCollection = new ObjectIdCollection(sourceIds);

  399.                     // 沿路径创建每个阵列副本
  400.                     for (int i = 0; i < positions.Length; i++)
  401.                     {
  402.                         double distance = positions;

  403.                         // 确保距离在路径范围内
  404.                         if (distance < 0 || distance > totalLength)
  405.                             continue;

  406.                         // 获取路径上的点
  407.                         Point3d pointOnPath;
  408.                         try
  409.                         {
  410.                             pointOnPath = path.GetPointAtDist(distance);
  411.                             // 确保点在二维平面
  412.                             pointOnPath = new Point3d(pointOnPath.X, pointOnPath.Y, 0.0);
  413.                         }
  414.                         catch
  415.                         {
  416.                             // 如果距离超出范围,跳过
  417.                             continue;
  418.                         }

  419.                         // 克隆源对象
  420.                         IdMapping mapping = new IdMapping();
  421.                         db.DeepCloneObjects(sourceIdCollection, db.CurrentSpaceId, mapping, false);

  422.                         // 对克隆的对象应用变换
  423.                         foreach (IdPair pair in mapping)
  424.                         {
  425.                             if (pair.IsCloned && pair.IsPrimary)
  426.                             {
  427.                                 Entity clonedEntity = tr.GetObject(pair.Value, OpenMode.ForWrite) as Entity;
  428.                                 if (clonedEntity != null)
  429.                                 {
  430.                                     // 1. 从基点平移到路径点(二维平移)
  431.                                     Vector3d translation = basePoint.GetVectorTo(pointOnPath);
  432.                                     Matrix3d transform = Matrix3d.Displacement(translation);

  433.                                     // 2. 如果需要,旋转至切线方向(二维旋转)
  434.                                     if (rotateWithPath)
  435.                                     {
  436.                                         try
  437.                                         {
  438.                                             double currentParam = path.GetParameterAtDistance(distance);
  439.                                             Vector3d currentTangent = path.GetFirstDerivative(currentParam).GetNormal();
  440.                                             
  441.                                             // 二维平面旋转角度
  442.                                             double currentRotation = Math.Atan2(currentTangent.Y, currentTangent.X);
  443.                                             double rotationAngle = currentRotation - startRotation;

  444.                                             // 以路径点为中心旋转(Z轴旋转)
  445.                                             if (Math.Abs(rotationAngle) > 1e-10) // 避免微小旋转
  446.                                             {
  447.                                                 transform = transform *
  448.                                                     Matrix3d.Rotation(rotationAngle, Vector3d.ZAxis, pointOnPath);
  449.                                             }
  450.                                         }
  451.                                         catch
  452.                                         {
  453.                                             // 如果无法计算旋转,继续执行平移
  454.                                         }
  455.                                     }

  456.                                     // 应用变换
  457.                                     clonedEntity.TransformBy(transform);
  458.                                 }
  459.                             }
  460.                         }
  461.                     }

  462.                     tr.Commit();
  463.                     return true;
  464.                 }
  465.                 catch (System.Runtime.InteropServices.SEHException sehEx)
  466.                 {
  467.                     ed.WriteMessage("\n系统异常: 路径操作失败。");
  468.                     return false;
  469.                 }
  470.                 catch (Autodesk.AutoCAD.Runtime.Exception ex)
  471.                 {
  472.                     ed.WriteMessage("\n创建阵列时出错:"+  ex.Message);
  473.                     return false;
  474.                 }
  475.             }
  476.         }
  477.     }
  478. }




网友答: 我先来,AI生成的就让AI去完善

网友答:
tranque 发表于 2026-1-4 12:23
我先来,AI生成的就让AI去完善

人家都没有说时ai生产的啊


网友答:
qifeifei 发表于 2026-1-4 14:09
人家都没有说时ai生产的啊

感觉就很像

网友答: 代码质量越来越差了,连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.
  • 上一篇:图面工具箱径向菜单
  • 下一篇:没有了