a117034423 发表于 2025-8-3 17:56:41

关于vla最小包围盒问题的求助

好久没有发言了,最近一直在学习高飞鸟的最小包围盒代码,对于连续尺寸标注对象,我用(vlax-invoke-method obj 'GetBoundingBox 'llp 'urp)为什么非正交的对齐标注生成的包围盒会比正交的大一圈,我参考了leemac的包围盒函数,他是用复制对象旋转与轴对齐之后再求包围盒,这样的画执行效率太慢,能否有大神给解决下,为什么带角度的标注包围盒会变大,代码我免费贡献出来了

;
; 获取标注对象的基准点和方向向量
(defun dim-basepoints (ent / dxf pt1 pt2 vec)
(setq dxf (entget ent)
      pt1 (cdr (assoc 10 dxf)); 标注起点
      pt2 (cdr (assoc 14 dxf)); 标注终点
      vec (mapcar '- pt2 pt1)   ; 方向向量
)
(list pt1 pt2 vec); 返回起点、终点和方向向量
)

;; 点集包围盒计算
;; 参数: pts - 点列表 [(x1 y1) (x2 y2) ...]
;; 返回: 包含左下角和右上角点的列表 [ ]
(defun Dim:pointsetboundingbox ( pts / minx miny maxx maxy pt )
    (setq minx (car (car pts))
          miny (cadr (car pts))
          maxx minx
          maxy miny
    )
    (foreach pt pts
      (setq minx (min minx (car pt))
            miny (min miny (cadr pt))
            maxx (max maxx (car pt))
            maxy (max maxy (cadr pt))
      )
    )
    (list (list minx miny) (list maxx maxy))
)

;; 旋转点集
;; 参数: lst - 点列表, bpt - 旋转基点, ang - 旋转角度(弧度)
;; 返回: 旋转后的点列表
(defun Dim:rotatepoints ( lst bpt ang / mat vec )
    (setq mat
      (list
            (list (cos ang) (sin (- ang)) 0.0)
            (list (sin ang) (cos ang)   0.0)
         '(0.0 0.0 1.0)
      )
    )
    (setq vec (mapcar '- bpt (mxv mat bpt)))
    (mapcar '(lambda ( x ) (mapcar '+ (mxv mat x) vec)) lst)
)

;; 矩阵与向量乘法
(defun mxv ( m v )
    (mapcar '(lambda ( r ) (apply '+ (mapcar '* r v))) m)
)

;; 计算与标注基线对齐的最小包围盒
;; 参数: ss - 选择集
;; 返回: 最小包围盒的四个顶点坐标列表 ((x1 y1) (x2 y2) (x3 y3) (x4 y4))
(defun CalculateAlignedDimensionBoundingBox (ss / allPoints basePoints baseVectors baseAngle minBox i e obj ll ur llp urp basePts avgVec rotMat invRotMat)
    (if (eq 'pickset (type ss))
      (progn
            (setq allPoints nil
                  basePoints nil
                  baseVectors nil)
            
            ;; 收集所有标注对象的点和基线
            (repeat (setq i (sslength ss))
                (setq e (ssname ss (setq i (1- i))))
                (setq obj (vlax-ename->vla-object e))
               
                ;; 检查是否为标注对象
                (if (wcmatch (strcase (vla-get-objectname obj)) "*DIMENSION")
                  (progn
                        ;; 获取标注的基准点和方向向量
                        (setq basePts (dim-basepoints e))
                        (setq basePoints (append basePoints (list (car basePts) (cadr basePts))))
                        (setq baseVectors (append baseVectors (list (caddr basePts))))
                        
                        ;; 获取标注对象的包围盒点
                        (vl-catch-all-apply
                           '(lambda ()
                              (vlax-invoke-method obj 'GetBoundingBox 'llp 'urp)
                            )
                        )
                        
                        (if llp
                            (progn
                              (setq ll (vlax-safearray->list llp))
                              (setq ur (vlax-safearray->list urp))
                              
                              ;; 添加包围盒的四个角点
                              (setq allPoints (append allPoints
                                    (list
                                        (list (car ll) (cadr ll))    ; 左下角
                                        (list (car ur) (cadr ll))    ; 右下角
                                        (list (car ur) (cadr ur))    ; 右上角
                                        (list (car ll) (cadr ur))    ; 左上角
                                    )
                              ))
                            )
                        )
                  )
                )
            )
            
            (if allPoints
                (progn
                  ;; 计算所有标注的平均方向向量
                  (if (and baseVectors (> (length baseVectors) 0))
                        (progn
                            ;; 计算平均向量
                            (setq avgVec (mapcar '(lambda (coord) (/ (apply '+ coord) (length baseVectors))) (transpose baseVectors)))
                            ;; 计算平均向量的角度
                            (setq baseAngle (angle '(0 0) (list (car avgVec) (cadr avgVec))))
                        )
                        (setq baseAngle 0.0); 默认角度
                  )
                  
                  ;; 计算所有点的中心点
                  (setq cen (mapcar '(lambda (coords) (/ (apply '+ coords) (length coords))) (transpose allPoints)))
                  
                  ;; 创建旋转矩阵 (将点集旋转到与基线对齐)
                  (setq rotMat
                        (list
                            (list (cos baseAngle) (sin baseAngle) 0.0)
                            (list (- (sin baseAngle)) (cos baseAngle) 0.0)
                            '(0.0 0.0 1.0)
                        )
                  )
                     
                  ;; 旋转点集使其与基线对齐
                  (setq rotatedPoints
                        (mapcar
                            '(lambda (pt)
                              (mxv rotMat (mapcar '- pt cen))
                           )
                            allPoints
                        )
                  )
                     
                  ;; 将旋转后的点移回中心点
                  (setq rotatedPoints
                        (mapcar
                            '(lambda (pt)
                              (mapcar '+ pt cen)
                           )
                            rotatedPoints
                        )
                  )
                  
                  ;; 计算旋转后点集的包围盒
                  (setq box (Dim:pointsetboundingbox rotatedPoints))
                  
                  ;; 创建包围盒的四个顶点
                  (setq boxPoints (list
                        (car box)                        ; 左下角
                        (list (cadr (cadr box)) (cadr (car box))); 右下角
                        (cadr box)                        ; 右上角
                        (list (car (car box)) (cadr (cadr box))); 左上角
                  ))

                  ;; 修正包围盒顶点顺序,确保正确闭合
                  (setq boxPoints (list
                        (list (car (car box)) (cadr (car box))); 左下角
                        (list (car (cadr box)) (cadr (car box))); 右下角
                        (list (car (cadr box)) (cadr (cadr box))); 右上角
                        (list (car (car box)) (cadr (cadr box))); 左上角
                  ))
                  
                  ;; 创建反向旋转矩阵
                  (setq invRotMat
                        (list
                            (list (cos (- baseAngle)) (sin (- baseAngle)) 0.0)
                            (list (- (sin (- baseAngle))) (cos (- baseAngle)) 0.0)
                            '(0.0 0.0 1.0)
                        )
                  )
                     
                  ;; 将包围盒旋转回原始角度
                  (setq minBox
                        (mapcar
                            '(lambda (pt)
                              (mapcar '+ (mxv invRotMat (mapcar '- pt cen)) cen)
                           )
                            boxPoints
                        )
                  )
                  
                  minBox
                )
                (progn
                  (princ "\n未找到标注对象。")
                  nil
                )
            )
      )
      (
            (princ "\n参数错误: 请提供有效的选择集。")
            nil
      )
    )
)

;; 矩阵转置
(defun transpose ( lst )
    (apply 'mapcar (cons 'list lst))
)

;; 测试函数: 创建选择集并计算标注对象的最小包围盒
(defun c:tt (/ ss bbox)
    (princ "\n请选择要计算最小包围盒的标注对象...")
    (setq ss (ssget '((0 . "*DIMENSION"))));; 只选择标注对象
   
    (if ss
      (progn
            (setq bbox (CalculateAlignedDimensionBoundingBox ss))
            
            (if bbox
                (progn
                  (princ (strcat "\n最小包围盒顶点: " (vl-princ-to-string bbox)))
                  
                  ;; 绘制包围盒
                  (entmake
            (append
               '(
                  (000 . "LWPOLYLINE")
                  (100 . "AcDbEntity")
                  (100 . "AcDbPolyline")
                  (090 . 4)
                  (070 . 1)
                )
                (mapcar '(lambda ( p ) (cons 10 p)) bbox)
            )
      )
                  bbox
                )
            )
      )
    )
    (princ)
)




a117034423 发表于 2025-8-4 12:52:45

(defun boundingbox (sel / idx obj dim-ent base-pts base-vec rot_angle base-pt llp urp min-pt max-pt bbox-pts)
    (if (> (sslength sel) 0)
    (progn
      (setq dim-ent (ssname sel 0));; 步骤1: 获取首个标注对象的尺寸基线
      (setq base-pts (dim-basepoints dim-ent)) ; 返回基点列表 (基点 pt4 起点 pt1)
      ;; 步骤2: 计算基线与X轴的夹角和向量
      (setq base-vec (mapcar '- (cadr base-pts) (car base-pts))) ; 基线向量
      (setq rot_angle (atan (cadr base-vec) (car base-vec))) ; 与X轴的夹角
      ;; 设置旋转基点
      (setq base-pt (car base-pts))
      ;; 步骤3: 判断是否需要旋转(检查是否为正交角度)
      (setq need-rotate
      (not (or
          (equal rot_angle 0.0 1e-6)            ; 0度
          (equal rot_angle (/ pi 2) 1e-6)         ; 90度
          (equal rot_angle pi 1e-6)               ; 180度
          (equal rot_angle (* 3 (/ pi 2)) 1e-6)   ; 270度
          (equal rot_angle (* 2 pi) 1e-6)         ; 360度
      )))
      ;; 步骤4: 如果需要,将所有对象旋转至与X轴对齐
      (if need-rotate
      (repeat (setq idx (sslength sel))
          (setq obj (vlax-ename->vla-object (ssname sel (setq idx (1- idx)))))      
          (vla-rotate obj (vlax-3d-point base-pt) (- rot_angle))
      )
      )
      ;; 步骤5: 计算选择集整体包围盒
      (repeat (setq idx (sslength sel))
      (setq obj (vlax-ename->vla-object (ssname sel (setq idx (1- idx)))))      
      (if (and (vlax-method-applicable-p obj 'getboundingbox)
               (not (vl-catch-all-error-p (vl-catch-all-apply 'vla-getboundingbox (list obj 'llp 'urp)))))      
          (progn
            (setq current-llp (vlax-safearray->list llp))
            (setq current-urp (vlax-safearray->list urp))
            
            ;; 更新整体包围盒的最小和最大点
            (if (not min-pt)
            (setq min-pt current-llp max-pt current-urp)
            (progn
                (setq min-pt (mapcar 'min min-pt current-llp))
                (setq max-pt (mapcar 'max max-pt current-urp))
            )
            )
          )
      )
      )
      ;; 步骤6: 使用辅助函数计算四点包围盒
      (setq bbox-pts (hc-pt2-to-pt4 min-pt max-pt))
      
      ;; 步骤7: 如果旋转过,将所有对象恢复至初始状态
      (if need-rotate
      (repeat (setq idx (sslength sel))
          (setq obj (vlax-ename->vla-object (ssname sel (setq idx (1- idx)))))      
          (vla-rotate obj (vlax-3d-point base-pt) rot_angle)
      )
      )
      ;; 步骤8: 将包围盒角点旋转回原始位置(如果需要)
      (if need-rotate
      (setq bbox-pts (hc-pts-rotate bbox-pts base-pt rot_angle))
      )
      
      ;; 返回选择集的整体包围盒
      bbox-pts
    )
    nil ; 如果选择集为空,返回nil
)
)
(defun dim-basepoints (ent / entls pt1 pt2 pt3 pt4 ang pt1a pt3a);获得Align/Rotate尺寸的基线--02
(setq entls (entget ent)
        pt1 (cdr (assoc 10 (entget ent)));(setq ent (car (entsel)))
        pt2 (cdr (assoc 14 (entget ent)))
        pt3 (cdr (assoc 13 (entget ent)))
      ang(angle pt1 pt2)
        pt1a (polar pt1 (+ ang (* pi 0.5)) 2)
        pt3a (polar pt3 ang 2)
        pt4 (inters pt1 pt1a pt3 pt3a nil)
        )(list pt4 pt1))
;;取两点的中点(二维)
(defun hc-mid(p1 p2)(mapcar '(lambda (x y) (* 0.5 (+ x y))) p1 p2))
;;pt点围绕p0点旋转ang弧度
(defun hc-pt-rotate (pt p0 ang)(polar p0 (+ (angle p0 pt) ang) (distance pt p0)))
;;点表围绕p0点旋转ang弧度
(defun hc-pts-rotate(lst p0 ang)(mapcar '(lambda(x)(hc-pt-rotate x p0 ang))lst))
;;根据两点坐标返回4个点坐标
(defun hc-pt2-to-pt4 (pt1 pt2)(list (list (car pt1)(cadr pt1)) (list(car pt2)(cadr pt1))(list (car pt2)(cadr pt2))(list (car pt1)(cadr pt2))))
;; 测试函数 - 命令名为TT
(defun c:TT (/ sel bbox-pts pt1 pt2 pt3 pt4)
"测试boundingbox函数的命令\n调用方式: 在AutoCAD命令行输入TT,然后选择要测试的标注对象"
;; 提示用户选择对象
(prompt "\n选择要计算包围盒的标注对象: ")
(setq sel (ssget))

(if sel
    (progn
      ;; 调用boundingbox函数
      (setq bbox-pts (boundingbox sel))
      
      (if bbox-pts
      (progn
          ;; 提取四个角点
          (setq pt1 (nth 0 bbox-pts))
          (setq pt2 (nth 1 bbox-pts))
          (setq pt3 (nth 2 bbox-pts))
          (setq pt4 (nth 3 bbox-pts))
         
          ;; 创建多段线来显示包围盒
          (entmake (list
            (cons 0 "LWPOLYLINE")
            (cons 100 "AcDbEntity")
            (cons 100 "AcDbPolyline")
          (cons 62 1)
            (cons 90 4); 4个顶点
            (cons 70 1); 闭合
            (cons 10 pt1)
            (cons 10 pt2)
            (cons 10 pt3)
            (cons 10 pt4)
          ))
         
          (prompt (strcat "\n包围盒已绘制,角点坐标: "
                        "\n1: (" (rtos (car pt1)) ", " (rtos (cadr pt1)) ")"
                        "\n2: (" (rtos (car pt2)) ", " (rtos (cadr pt2)) ")"
                        "\n3: (" (rtos (car pt3)) ", " (rtos (cadr pt3)) ")"
                        "\n4: (" (rtos (car pt4)) ", " (rtos (cadr pt4)) ")"
                  ))
      )
      (prompt "\n无法计算包围盒,请确保选择了有效的标注对象。")
      )
    )
    (prompt "\n未选择任何对象。")
)
(princ)
)
我能想到的办法就只有旋转对象了,其他方法我测试完就是不好使

XDSoft 发表于 2025-8-6 05:21:12

不是尺寸的对象,你测试过没?
你仔细看看尺寸的,是不是尺寸的定义点在外面,包围盒要计算到定义点,而不是线

a117034423 发表于 2025-8-6 10:03:19

XDSoft 发表于 2025-8-6 05:21
不是尺寸的对象,你测试过没?
你仔细看看尺寸的,是不是尺寸的定义点在外面,包围盒要计算到定义点,而不 ...

别的我测试是没问题的,主要是大出去的那一圈我也没看明白是啥原因大的,也不像扩出去的点,就是被vla放大了。我用笨办法解决了,纯数学算的,取出标注的基点再根据界线和文字包围盒偏移

pxt2015 发表于 2025-8-6 10:24:45

在wcs返回平行于尺寸标注方向的包围盒
晓东API(CAD尺寸标注有效,天正尺寸标注,达不到预期效果)
(setq ss(ssget)
      dir (Xdrx_Entity_direction (ssname ss 0))
)
(setq bwh (Xdrx_Entity_Box ss dir))

a117034423 发表于 2025-8-6 11:22:23

pxt2015 发表于 2025-8-6 10:24
在wcs返回平行于尺寸标注方向的包围盒
晓东API(CAD尺寸标注有效,天正尺寸标注,达不到预期效果)
(setq s ...

我现在用的就是api 只不过想研习一下 尺寸标注的包围盒

统一网名 发表于 2025-8-25 11:09:45

本帖最后由 统一网名 于 2025-8-25 11:11 编辑

a117034423 发表于 2025-8-4 12:52
我能想到的办法就只有旋转对象了,其他方法我测试完就是不好使

页: [1]
查看完整版本: 关于vla最小包围盒问题的求助