jezli 发表于 2025-2-27 18:36:36

查找多层嵌套块内文本对象,并正确绘制圆形标记

主要目的在于检索当前dwg文件中的某些字符,并在其上绘制圆形便于查看标记。
目前我能成功在单行文本、多行文本、以及单层块内实现这个目的,但是无法在多层嵌套块中正确位置绘制圆形。而且每次多层嵌套块绘制出来的圆形都在图纸原点,我很q


(defun c:DrawCircleAtBlockText (/ *error* main acadDoc blks str dia)
(vl-load-com)
(setq foundCount 0); 设置计数器为全局变量

;; 错误处理函数
(defun *error* (msg)
    (if acadDoc (vla-EndUndoMark acadDoc))
    (princ (strcat "\n 错误: " msg))
    (princ)
)

;; 创建或获取图层
(defun setup-layer (layerName colorIndex / layerObj layers)
    (setq layers (vla-get-Layers
                   (vla-get-ActiveDocument
                     (vlax-get-acad-object))))
   
    ;; 尝试获取已存在的图层
    (if (not (vl-catch-all-error-p
               (setq layerObj (vl-catch-all-apply 'vla-item
                                                 (list layers layerName)))))
      layerObj
      ;; 如果图层不存在,创建新图层
      (setq layerObj (vla-add layers layerName))
    )
   
    ;; 设置图层颜色
    (vla-put-Color layerObj colorIndex)
    layerName
)

;; 计算总块数
(defun count-total-blocks (blks / count)
    (setq count 0)
    (vlax-for blk blks
      (setq count (1+ count))
    )
    count
)

;; 更新进度条
(defun update-progress (current total / percent)
    (setq percent (fix (* (/ current total) 100.0)))
    (princ (strcat "\r处理进度: " (itoa percent) "%"))
    (if (= current total) (princ "\n"))
)

;; 主处理函数
(defun main ()
    (setq acadDoc (vla-get-ActiveDocument (vlax-get-acad-object))
          blks (vla-get-Blocks acadDoc))
   
    ;; 处理图层
    (setup-layer "M-0-E" 6)
   
    ;; 获取并验证文字输入
    (while (not str)
      (setq str (getstring "\n请输入需要查找的文字内容: "))
      (cond
      ((= str "")
          (setq str nil)
          (princ "\n 错误: 文字内容不能为空,请重新输入")
      )
      ((= str nil)
          (exit)
      )
      )
    )
   
    ;; 获取并验证直径输入
    (while (not dia)
      (setq dia (getdist "\n请输入圆形直径: "))
      (cond
      ((and dia (<= dia 0.0))
          (setq dia nil)
          (princ "\n 错误: 直径必须大于0,请重新输入")
      )
      ((= dia nil)
          (exit)
      )
      )
    )
   
    (vla-StartUndoMark acadDoc)
   
    (if (and str dia)
      (progn
      (setq totalBlocks (count-total-blocks blks)
            currentBlock 0)
      (princ "\n 开始处理,总块数: ")
      (princ totalBlocks)
      (vlax-for blkDef blks
          (setq currentBlock (1+ currentBlock))
          (update-progress currentBlock totalBlocks)
          (process-block-def blkDef str dia)
      )
      (princ (strcat "\n 已成功创建 " (itoa foundCount) " 个圆形"))
      )
      (princ "\n 操作已取消")
    )
    (vla-EndUndoMark acadDoc)
)

       ;; 块定义处理
    (defun process-block-def (blkDef str dia / found)
      (princ (strcat "\n 正在处理块定义: " (vla-get-Name blkDef)))
      
      ;; 首先处理普通文本对象
      (vlax-for obj blkDef
      (cond
          ((or (= (vla-get-ObjectName obj) "AcDbText")
               (= (vla-get-ObjectName obj) "AcDbMText"))
         (princ (strcat "\n 找到文本对象,内容: " (vla-get-TextString obj)))
         (if (= (strcase (vla-get-TextString obj)) (strcase str))
             (progn
               (princ "\n 文本内容匹配成功")
               (create-circle-at-text obj dia)
               (setq found t)
             )
         ))
          ;; 处理块参照
          ((= (vla-get-ObjectName obj) "AcDbBlockReference")
         (princ "\n 找到块参照")
         (if (process-block-reference obj str dia)
             (setq found t)
         ))
      )
      )
      found
    )

    ;; 在普通文本位置创建圆
    (defun create-circle-at-text (textObj dia / textPt)
      (setq textPt (vlax-get textObj 'InsertionPoint))
      (princ (strcat "\n 文本坐标: " (vl-princ-to-string textPt)))
      
      (if (entmakex
            (list
            '(0 . "CIRCLE")
            '(8 . "M-0-E")
            (cons 10 textPt)
            (cons 40 (/ dia 2.0))
            )
          )
      (progn
          (setq foundCount (1+ foundCount))
          (princ "\n 创建圆形成功"))
      (princ "\n 创建圆形失败"))
    )
   
    ;; 处理块参照中的文本
    (defun process-block-reference (blkRef str dia / blkDef found)
      (if (setq blkDef (vla-item blks (vla-get-Name blkRef)))
      (progn
          (princ (strcat "\n 处理块参照: " (vla-get-Name blkRef)))
          (vlax-for obj blkDef
            (cond
            ;; 处理块中的文本
            ((or (= (vla-get-ObjectName obj) "AcDbText")
                   (= (vla-get-ObjectName obj) "AcDbMText"))
               (princ (strcat "\n 块中找到文本,内容: " (vla-get-TextString obj)))
               (if (= (strcase (vla-get-TextString obj)) (strcase str))
               (progn
                   (princ "\n 块中文本匹配成功")
                   (create-circle-at-block-text obj blkRef dia)
                   (setq found t)
               )
               ))
            ;; 处理嵌套块
            ((= (vla-get-ObjectName obj) "AcDbBlockReference")
               (if (process-block-reference obj str dia)
               (setq found t)
               ))
            )
          )
      )
      )
      found
    )
   
    ;; 在块参照中的文本位置创建圆
    (defun create-circle-at-block-text (textObj blkRef dia / textPt mat worldPt)
      (setq textPt (vlax-get textObj 'InsertionPoint))
      (princ (strcat "\n 块中文本局部坐标: " (vl-princ-to-string textPt)))
      
      ;; 获取块的变换矩阵
      (setq mat (get-cumulative-matrix blkRef))
      (setq worldPt (apply-matrix textPt mat))
      (princ (strcat "\n 转换后的世界坐标: " (vl-princ-to-string worldPt)))
      
      (if (entmakex
            (list
            '(0 . "CIRCLE")
            '(8 . "M-0-E")
            (cons 10 worldPt)
            (cons 40 (/ dia 2.0))
            )
          )
      (progn
          (setq foundCount (1+ foundCount))
          (princ "\n 创建圆形成功"))
      (princ "\n 创建圆形失败"))
    )
   
    ;; 实例处理(用于块参照中的文本对象)
    ;; 将未使用的函数转换为注释
    ;; ===== 以下为未使用的函数,已注释 =====
    ;; process-block-instances 函数定义
    #|
    (defun process-block-instances (blkDef textObj dia / ss refID mat textPtWorld)
      (princ (strcat "\n 处理块实例: " (vla-get-Name blkDef)))
      ;; 获取文本在块定义中的局部坐标
      (setq textPt (vlax-get textObj 'InsertionPoint))
      (princ (strcat "\n 文本局部坐标: " (vl-princ-to-string textPt)))
      
      ;; 处理所有块实例
      (setq blkName (vla-get-Name blkDef)
            ss (ssget "_X" (list '(0 . "INSERT") (cons 2 blkName))))
      
      (if ss
      (progn
          (princ (strcat "\n 找到 " (itoa (sslength ss)) " 个块实例"))
          (repeat (setq cnt (sslength ss))
            (setq refID (ssname ss (setq cnt (1- cnt))))
            
            ;; 转换为VLA对象并应用变换
            (if (setq refID (vlax-ename->vla-object refID))
            (progn
                (setq mat (get-cumulative-matrix refID))
                (setq textPtWorld (apply-matrix textPt mat))
                (princ (strcat "\n 转换后的世界坐标: " (vl-princ-to-string textPtWorld)))
               
                ;; 创建圆
                (if (entmakex
                      (list
                        '(0 . "CIRCLE")
                        '(8 . "M-0-E")
                        (cons 10 textPtWorld)
                        (cons 40 (/ dia 2.0))
                      )
                  )
                  (progn
                  (setq foundCount (1+ foundCount))
                  (princ "\n 创建圆形成功"))
                  (princ "\n 创建圆形失败"))
            )
            )
          )
      )
      (princ "\n 未找到块实例")
      )
    )
   |#
    ;; 获取累积变换矩阵(递归处理嵌套块)
    (defun get-cumulative-matrix (blkRef / parentMatrix parentEnt)
      (setq parentMatrix (get-block-transform blkRef))
      
      ;; 获取当前块参照的所有者(父块)
      (if (and blkRef
               (setq parentEnt (vlax-vla-object->ename blkRef))
               (setq parentEnt (entnext parentEnt))
               (= "INSERT" (cdr (assoc 0 (entget parentEnt)))))
      ;; 如果还在嵌套块中,继续向上递归
      (if (setq parentObj (vlax-ename->vla-object parentEnt))
          (setq parentMatrix (multiply-matrices
                              (get-cumulative-matrix parentObj)
                              parentMatrix))
      )
      )
      parentMatrix
    )
   
    ;; 获取单个块的变换矩阵(平移、旋转、缩放)
    (defun get-block-transform (blkRef / insPt scaleX scaleY rotAng mat)
      (setq insPt(vlax-get blkRef 'InsertionPoint)
            scaleX (vla-get-XScaleFactor blkRef)
            scaleY (vla-get-YScaleFactor blkRef)
            rotAng (vla-get-Rotation blkRef))
      
      ;; 构建变换矩阵(4x4齐次矩阵)
      (setq mat (list
               (list (* scaleX (cos rotAng)) (* (- scaleY) (sin rotAng)) 0.0 (car insPt))
               (list (* scaleX (sin rotAng)) (* scaleY (cos rotAng)) 0.0 (cadr insPt))
               (list 0.0 0.0 1.0 (if (caddr insPt) (caddr insPt) 0.0))
               (list 0.0 0.0 0.0 1.0)))
      mat
    )
   
    ;; 矩阵乘法(合并嵌套变换)
    (defun multiply-matrices (mat1 mat2 / result)
      (setq result (mapcar '(lambda (row)
                              (mapcar '(lambda (col)
                                        (apply '+ (mapcar '* row col)))
                              (apply 'mapcar (cons 'list mat2))))
                        mat1))
      result
    )
    ;; 应用矩阵变换到点
      (defun apply-matrix (pt mat / x y z)
          (setq x (car pt)
                y (cadr pt)
                z (caddr pt))
          (list
            (+ (* x (caar mat)) (* y (cadar mat)) (* z (caddar mat)) (cadddr (car mat)))
            (+ (* x (caadr mat)) (* y (cadadr mat)) (* z (caddr (cadr mat))) (cadddr (cadr mat)))
            (+ (* x (caaddr mat)) (* y (cadadr mat)) (* z (caddr (caddr mat))) (cadddr (caddr mat))))
      )
    #|
    ;; 增强嵌套块处理(递归遍历所有层级)
    (defun process-nested-block (blkDef str dia / obj)
      (vlax-for obj blkDef
      (cond
          ((= (vla-get-ObjectName obj) "AcDbBlockReference")
            (process-block-def
            (vla-item (vla-get-Blocks (vla-get-ActiveDocument (vlax-get-acad-object)))
            (vla-get-Name obj))
            str dia)
          )
      )
      )
    )
    |#
    (main)
    (princ)
)

xyp1964 发表于 2025-2-27 18:36:37


ssyfeng 发表于 2025-2-28 11:42:24

本帖最后由 ssyfeng 于 2025-3-1 11:08 编辑

转换一下坐标应该就可以

kozmosovia 发表于 2025-2-28 11:44:39

嵌套块要不停地计算嵌套矩阵再转换。比较简单的方式是将图块弄到DBX中,逐层分解到没有图块,然后过滤全图找到需要标记的文本,再根据文本位置在DWG中创建圆。

jezli 发表于 2025-2-28 14:28:46

ssyfeng 发表于 2025-2-28 11:42
转换一下坐标应该就可以

我试了坐标转换,单层块没问题 多层嵌套块坐标转换画出来的圆形还是不在其位,会偏离,昨天弄了半天也没有解决。

jezli 发表于 2025-2-28 14:29:51

kozmosovia 发表于 2025-2-28 11:44
嵌套块要不停地计算嵌套矩阵再转换。比较简单的方式是将图块弄到DBX中,逐层分解到没有图块,然后过滤全图 ...

哎,这不是我想要的结果,会破坏原图的完整性。但这确实是最简单的做法。

kozmosovia 发表于 2025-2-28 14:32:11

jezli 发表于 2025-2-28 14:29
哎,这不是我想要的结果,会破坏原图的完整性。但这确实是最简单的做法。

不需要破坏DWG,explode是在DBX中进行的,对DWG没有任何影响

lijiao 发表于 2025-2-28 14:56:15

楼主搞得过于复杂,实际上不存在嵌套块的处理

帮你修改了一下
(defun c:DrawCircleAtBlockText (/       ACADDOC    BLKS
      CIRCLE       DIA    FOUNDCOUNT
      STR       TEXTPT    TOTALBLOCKS
      *error*       process-block-def
             )
(vl-load-com)

;; 错误处理函数
(defun *error* (msg)
    (ifacadDoc
      (vla-EndUndoMark acadDoc)
    )
    (princ (strcat "\n 错误: " msg))
    (princ)
)
;; 块定义处理
(defun process-block-def (blkDef str dia / found)
    (setq found 0)
    (vlax-for obj blkDef
      (if (and (or (= (vla-get-ObjectName obj) "AcDbText")
       (= (vla-get-ObjectName obj) "AcDbMText")
         )
         (= (strcase (vla-get-TextString obj)) (strcase str))
    )
(progn
    (setq found (1+ found))
    (setq textPt (vla-get-InsertionPoint obj))
    (setq circle (vla-addcircle blkDef textPt (* dia 0.5)))
    (vla-put-layer circle "M-0-E")
)
      )
    )
    found
)


(setq foundCount 0)      ; 设置计数器为全局变量
(setqacadDoc(vla-get-ActiveDocument (vlax-get-acad-object))
blks(vla-get-Blocks acadDoc)
)

;; 处理图层
(if (not (tblsearch "layer" "M-0-E"))
    (entmake '((0 . "LAYER")
         (100 . "AcDbSymbolTableRecord")
         (100 . "AcDbLayerTableRecord")
         (70 . 0)
         (6 . "Continuous")
         (2 . "M-0-E")
         (62 . 6)
      )
    )
)
;; 获取并验证文字输入
(while (= (setq str (getstring "\n请输入需要查找的文字内容: ")) "")
    (princ "\n 错误: 文字内容不能为空,请重新输入")
)

;; 获取并验证直径输入
(while (<= (setq dia (getdist "\n请输入圆形直径: ")) 0.0)
    (princ "\n 错误: 直径必须大于0,请重新输入")
)

(vla-StartUndoMark acadDoc)
(setq totalBlocks (vla-get-count blks))
(acet-ui-progress "开始处理..." totalBlocks)
(vlax-for blkDef blks
    (acet-ui-progress -1)
    (setq foundCount
   (+ foundCount (process-block-def blkDef str dia))
    )
)
(acet-ui-progress)
(princ
    (strcat "\n 已成功创建 " (itoa foundCount) " 个圆形")
)
(vla-EndUndoMark acadDoc)
(vla-Regen acadDoc acActiveViewport)
(princ)
)

jezli 发表于 2025-3-3 22:22:52

xyp1964 发表于 2025-2-27 18:36


求大佬赐教 点播
页: [1]
查看完整版本: 查找多层嵌套块内文本对象,并正确绘制圆形标记