77077 发表于 前天 11:27

沿路径阵列工具

求助,万能的网友
以下是一段沿路径阵列的代码,程序总是出错,我的需求是:
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;
                }
            }
      }
    }
}


tranque 发表于 前天 12:23

我先来,AI生成的就让AI去完善 :lol

qifeifei 发表于 前天 14:09

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

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

tranque 发表于 前天 14:19

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

感觉就很像:lol

你有种再说一遍 发表于 前天 17:53

代码质量越来越差了,连tr.GetObject(id)都不放using了...
而且开了n个事务,还混开了 StartOpenCloseTransaction...
不知道怎么评价

小鸟 发表于 前天 17:55

楼主 这个代码怎么加载到CAD里没接触过求教

你有种再说一遍 发表于 前天 17:57

本帖最后由 你有种再说一遍 于 2026-1-4 17:59 编辑

小鸟 发表于 2026-1-4 17:55
楼主 这个代码怎么加载到CAD里没接触过求教
去b站搜cad c#二次开发,需要下载vs(不是vs code),编译为dll.

77077 发表于 昨天 09:13

你有种再说一遍 发表于 2026-1-4 17:53
代码质量越来越差了,连tr.GetObject(id)都不放using了...
而且开了n个事务,还混开了 StartOpenCloseTransa ...

你说的是 这个么?
using (Transaction tr = db.TransactionManager.StartTransaction())
{
    try
    {
      tr.Commit();
    }
}

d1742647821 发表于 昨天 09:50

还要带界面,那就要耗费一些时间了,不是一个简单评论就能回答的完的,不如发到编程申请或者付费开发区找人帮你重写一个

你有种再说一遍 发表于 昨天 18:32

77077 发表于 2026-1-5 09:13
你说的是 这个么?
using (Transaction tr = db.TransactionManager.StartTransaction())
{


去看看这个项目吧,test文件夹下面都是案例,
写了大量的命令了.
https://gitee.com/inspirefunction/ifoxcad
页: [1] 2
查看完整版本: 沿路径阵列工具