吴可可123头像

AutoLISP轻松移动多段线顶点

在 CAD 二次开发中,AutoLISP 和 Python (pyautocad/comtypes) 是操作多段线顶点最常用且高效的两种语言。针对“选中顶点并水平向右移动 1 毫米”的需求,AutoLISP 因其内置于 AutoCAD、无需环境配置且直接支持实体数据修改(DXF 码操作),是首选方案;Python 则适合需要复杂逻辑或外部库支持的场景。

核心操作逻辑均为:获取多段线对象 $\rightarrow$ 拾取点定位最近顶点索引 $\rightarrow$ 读取该顶点坐标 $\rightarrow$ X 坐标增加 1.0 $\rightarrow$ 写回新坐标。

语言对比与选择建议

特性AutoLISP (推荐)Python (pyautocad/comtypes)C# (.NET)
运行环境AutoCAD 原生内置,加载即用需安装 Python 及 pyautocad 或 comtypes 库需编译 DLL,加载到 CAD 进程
顶点访问方式直接操作 DXF 数据组码 (Code 10)通过 COM 接口 Coordinates 数组通过 ObjectId 和事务处理 (Transaction)
代码复杂度低,几行代码即可完成中,需处理数组索引和 COM 连接高,需编写完整的类和方法
执行效率极高,直接内存操作中,COM 调用有开销高,原生 API 调用
适用场景快速脚本、命令行工具、轻量级插件数据分析、批量处理、跨软件交互大型商业插件、复杂 UI 交互

方案一:AutoLISP 实现(最简捷)

此方法利用 entsel 获取用户点击位置,自动计算最近顶点索引,直接修改多段线的 DXF 数据中的坐标组码。

(defun c:MoveVertexRight ()
  ;; 1. 提示用户选择多段线并指定靠近哪个顶点 (princ "
请选择多段线并点击靠近要移动的顶点:")
  (setq selData (entsel "
选取:"))  (if selData
    (progn
      ;; 2. 提取实体名和拾取点坐标
      (setq entName (car selData))
      (setq pickPt (cadr selData))
      
      ;; 3. 获取实体数据列表 (DXF 数据)
      (setq entData (entget entName))      ;; 4. 检查是否为多段线 (LWPOLYLINE 代码 100 为 "AcDbPolyline" 或 POLYLINE)
      (if (or (= (cdr (assoc 0 entData)) "LWPOLYLINE")
              (= (cdr (assoc 0 entData)) "POLYLINE"))
        (progn
          ;; 5. 获取所有顶点坐标 (组码 10)
          ;; 对于 LWPOLYLINE,顶点数据分散在多个 (10 x y) 组中
          (setq vertList nil)
          (foreach item entData
            (if (= (car item) 10)
              (setq vertList (cons (cdr item) vertList))
            )
          )
          ;; 反转列表以保持原始顺序 (entget 是从后往前的)
          (setq vertList (reverse vertList))
          
          ;; 6. 查找距离拾取点最近的顶点索引
          (setq minDist 1e99) ; 初始化最小距离为极大值
 (setq targetIdx -1)
          (setq currentIdx 0)
          
          (foreach pt vertList
            (setq dist (distance pt pickPt))
            (if (< dist minDist) (progn
                (setq minDist dist)
                (setq targetIdx currentIdx)
              )
            )
            (setq currentIdx (1+ currentIdx))
          )
          
          (if (>= targetIdx 0)
            (progn
              ;; 7. 获取目标顶点旧坐标
              (setq oldPt (nth targetIdx vertList))              ;; 8. 计算新坐标:X 轴增加 1.0毫米 (假设当前单位为毫米)
              ;; (x+1, y, z)
              (setq newPt (list (+ (car oldPt) 1.0) (cadr oldPt) (caddr oldPt)))
              
              ;; 9. 替换 DXF 数据中的对应坐标
              ;; 需要找到第 targetIdx 个组码为 10 的数据并替换
              (setq count -1)
              (setq newData (mapcar
                '(lambda (item)
 (if (= (car item) 10)
                     (progn
                       (setq count (1+ count))
                       (if (= count targetIdx)
                         (cons 10 newPt) ; 替换为目标新坐标
                         item            ; 保持原样
                       )                     )
                     item
                   )
                 )
                entData
              ))
              
              ;; 10. 将修改后的数据写回实体
              (entmod newData)
              
              ;; 11. 刷新屏幕显示
              (entupd entName)
              (princ (strcat "
成功移动第 " (itoa (1+ targetIdx)) " 个顶点向右 1mm。"))
            )
            (princ "
未找到有效顶点。")
          )
        )
        (princ "
所选对象不是多段线。")
      )
    )
    (princ "
未选择任何对象。")
  )
  (princ)
)

方案二:Python 实现 (基于 pyautocad)

若需在外部脚本或复杂系统中集成,可使用 Python。此方法通过 COM 接口访问 Coordinates 属性数组。注意:轻多段线 (AcDbPolyline)的坐标数组是扁平的 $(x_1, y_1, x_2, y_2, ...)$,索引需乘以 2。

from pyautocad import Autocad, APoint
import math

def move_vertex_right():
    acad = Autocad(create_if_not_exists=True)    # 1. 提示用户选择对象 (阻塞直到用户选择)
 # GetEntity 返回 (Object, PickPoint)
    try:
        obj, pick_pt = acad.doc.Utility.GetEntity(None, None)
    except Exception:
        print("未选择任何对象。")
 return

    # 2. 验证对象类型是否为多段线
    if obj.ObjectName not in ['AcDbPolyline', 'AcDb3dPolyline']:
        print("所选对象不是多段线。")
        return

    # 3. 获取所有坐标数据 (扁平数组: [x1, y1, z1, x2, y2, z2...])
    # 注意:LWPOLYLINE 通常只有 x, y,z 默认为 0 或省略,具体取决于实现
    coords = list(obj.Coordinates)
    
    # 4. 确定坐标分量数 (2D 多段线通常为 2,3D 为 3)
    # AcDbPolyline 通常是 2D 的,每个点 2 个值
    num_points = len(coords) // 2
    step = 2 if num_points * 2 == len(coords) else 3
    
    # 5. 寻找距离拾取点最近的顶点索引
    min_dist = float('inf')
 target_index = -1
    
    pick_x, pick_y = pick_pt[0], pick_pt[1]
    
    for i in range(num_points):
        idx_x = i * step
        idx_y = i * step + 1
        
        vx = coords[idx_x]
        vy = coords[idx_y]
        
        # 计算欧几里得距离
        dist = math.sqrt((vx - pick_x)**2 + (vy pick_y)**2)
        
        if dist < min_dist:
            min_dist = dist
            target_index = i
            
    if target_index != -1:
        # 6. 修改坐标:X 轴 + 1.0
        idx_x = target_index * step
        coords[idx_x] += 1.0
        
        # 7. 写回数组
        # pyautocad 需要转换为 VT_ARRAY of DOUBLE
        obj.Coordinates = coords
        obj.Update() # 刷新对象显示
        print(f"成功移动第 {target_index + 1} 个顶点向右 1mm。")
    else:
        print("无法定位顶点。")

if __name__ == "__main__":
    move_vertex_right()

关键技术解析

  1. 顶点定位算法:
    由于 API 通常不直接提供“获取鼠标点击处的顶点”函数,必须遍历多段线的所有顶点坐标,计算每个顶点与鼠标拾取点(Pick Point)之间的欧几里得距离 $d = \sqrt{(x_1-x_2)^2 + (y_1-y_2)^2}$,距离最小的即为目标顶点 。

  2. 数据结构差异:

    • AutoLISP:操作的是 DXF 组码列表。对于 LWPOLYLINE,每个顶点的 X,Y 坐标存储在组码 10 中,列表中会有多个 10 组,需按顺序匹配第 N 个 。
    • Python/C#:操作的是坐标数组。LWPOLYLINE 的 Coordinates 属性是一个双精度浮点数的一维数组,格式为 $[x_1, y_1, x_2, y_2, \dots]$。修改时需准确计算索引偏移量(Index Offset),例如第 $i$ 个点的 X 坐标索引为 $i \times 2$。
  3. 单位与坐标系:
    代码中直接 + 1.0 假设当前 CAD 绘图单位为毫米。如果图纸单位为米,则需 + 0.001。此外,操作是在当前 UCS(用户坐标系)下进行的,若多段线位于倾斜的 UCS 中,直接修改 X 坐标可能不会呈现“水平向右”的视觉效果,此时需先将 WCS 坐标转换至 OCS(对象坐标系)进行修改后再转回,但上述示例针对最常见的 WCS 水平移动场景 。


参考来源

 

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/m0_66048236/article/details/170193618

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--