caoyin 发表于 昨天 18:08

关于矩阵变换的几个配套函数,高手请忽略

本帖最后由 caoyin 于 2025-12-11 19:27 编辑

话题引自网友 xiaocainiao 的帖子,高手请绕行
http://bbs.mjtd.com/forum.php?mo ... 4232&extra=page%3D1



简单描述:
圆是块A的子图元,块A是块B的子图元,
块参照B是当前空间的对象,要求获取块参照A的插入点的UCS坐标并转换到当前空间。

为了把问题说清楚,我把上面的应用场景再稍加复杂化


圆是块定义A的子图元,块参照A是块定义B的子图元,块参照B是块定义C的子图元
块参照C是当前空间的对象,要求获取块参照A的插入点的UCS坐标并转换到当前空间坐标系。


1.nentselp函数可以获取最底层的对象。
(setq sel (nentselp "\n选择圆(多重套嵌块的子图元):"))
返回的四个元素:
1).圆的图元名
2).拾取点UCS
3).最底层对象变换到当前空间的4*4变换矩阵
4).父系块图元名列表(块参照C图元名 块参照B图元名 块参照A图元名)

2.如果我们想把圆心的坐标转换到当前空间,使用nentselp返回的变换矩阵就可以了,因为圆心是最底层对象

3.我们现在要获得的块参照A的的插入点,并转换到当前空间坐标系,而块参照A不是最底层的对象,因此nentselp提供的变换矩阵不配套。

4.获得变换矩阵:
(1)获得块参照A到块定义A的逆变换矩阵。
我们直接使用gile大神的 RevRefGeom 函数得到3*4变换矩阵(3*3矩阵+位移向量):
(setq mat1 (RevRefGeom (last (last sel))))
(2) 获得最底层对象变换到当前空间的4*4变换矩阵,
(setq mat2 (caddr sel));;此变换矩阵等价于gile大神的 RefGeom 函数(格式不同)

5.变换矩阵格式的处理
我们可以两次进行矩阵变换,也可以将矩阵合并进行一次变换。
但从第四步可以看出mat1是3*4变换矩阵,mat2是AutoCAD标注格式的变换矩阵。
我们可以把mat1为4*4变换矩阵,或者把mat2转换成3*4变换矩阵
由于AutoCAD针对图形对象的矩阵变换使用的是4*4格式,我们遵循这个惯例把3*4矩阵转换为4*4矩阵。

(setq mat1 (apply 'LT:Tmatrix-3x4->4x4 mat1))
LT:Tmatrix-3x4->4x4是我自用函数,在后面分享

引用大神的矩阵相乘函数MxM,合并矩阵,得到我们最终需要的变换矩阵
(MxM mat1 mat2)

这里做一个总结,无论块的套嵌级别有多深,我们不需要获取每一层套嵌块的变换矩阵,因此上文的块参照B可以不参与计算(计算已经在ACAD内部完成)以上是为了把问题说明白,更直接的方法见三楼(无需两次使用矩阵)6.全部转换过程

(defun C:TT (/ sel enx)
(setq sel (nentselp "\n选择圆(多重套嵌块的子图元): ")
      enx (entget (last (last sel)))
)
;; M4xP函数是我自用的应用4*4矩阵变换函数,在后面分享
(M4xP
      ;;把块参照A的OCS插入点转换为UCS点(原帖帖主的要求,改成0即是WCS点)
      (trans (cdr (assoc 10 enx)) (cdr (assoc 210 enx)) 1)      ;;把两个变换矩阵合并
      (MxM (apply 'LT:Tmatrix-3x4->4x4 (RevRefGeom (last (last sel))))
               (caddr sel)
      )
)
)


7.矩阵变换的相关配套函数
;;;先分享几个我自用的函数---------------------------------------------------

;; [功能] 将点应用3*3变换矩阵到向量(点的3x3仿射变换)
(defun M3xP (P M V) (mapcar '+ (MxV M P) V))


;; [功能] 将点应用4*4变换矩阵(点的4x4齐次变换)
;; 注意:以下函数返回的矩阵格式不同:
;;      nentselp 返回的矩阵: 直接使用 (M4xP P Mat)
;;      nentsel返回的矩阵: 需要转置 (M4xP P (TRP Mat))
;; nentsel函数帮助说明:
;; nentsel是唯一一个使用这种类型矩阵的 AutoLISP 函数。(非标准的ACAD4*4矩阵格式)
(defun M4xP (P M / W)
(setq P (MxV M (append P '(1.0)))
      W (last P)
)
(if (equal W 0.0 1E-10)
    (setq W 1.0)
)
(mapcar '/ P (list W W W))
)

;; [功能] 将点按照指定的矩阵变换
;; [参数] PT - 点
;;      MAT - 3x3矩阵或4x4矩阵
;;      VET - 向量。该参数为真,MAT被认为是3x3矩阵;为nil,MAT则被认为是4x4矩阵。
(defun LT:PointTransformBy (P MAT VEC)
(if VEC
    (M3xP P MAT VEC)
    (M4xP P MAT)
)
)



;; [功能] 将3x4矩阵(3x3矩阵+向量)转换为4x4矩阵
;; (LT:Tmatrix-3x4->4x4 '( (1 0 0) (0 1 0) (0 0 1)   (0 0 0)) nil)
;; (LT:Tmatrix-3x4->4x4 '(((1 0 0) (0 1 0) (0 0 1))(0 0 0)) nil)
;; (LT:Tmatrix-3x4->4x4 '( (1 0 0) (0 1 0) (0 0 1)) '(0 0 0))
(defun LT:Tmatrix-3x4->4x4(M V)
(cond
    (V)
    ((setq V (last M))
   (if (= (length M) 2)
       (setq M (car M))
   )
    )
)
(append (mapcar '(lambda (A B) (append A (list B))) M V)
          '((0.0 0.0 0.0 1.0))
)
)


;;;各路大神的的函数---------------------------------------------------

;; [功能] 向量点积
(defun VxV (V1 V2) (apply '+ (mapcar '* V1 V2)))

;; [功能] 向量数乘(向量x标量)
(defun VxS (V S) (mapcar '(lambda (N) (* N S)) V))

;; [功能] 应用变换矩阵到向量 --- Vladimir Nesterovsky
(defun MxV (M V) (mapcar '(lambda (R) (VxV R V)) M))


;; [功能] 矩阵相乘 --- Vladimir Nesterovsky
;; 在两个参数顺序不能错,顺序错了,结果也错了
(defun MxM (M Q) (mapcar (function (lambda (R) (MxV (TRP Q) R))) M))


;; [功能] 矩阵转置 --- Doug Wilson
(defun TRP (M) (apply 'mapcar (cons 'LIST M)))

;; [功能] 创建一个多维初始矩阵
;; [参数] N --- 矩阵维数
;; Identity Matrix-Lee Mac
;; Args: n - matrix dimension
(defun IMat (N / I J L M)
(repeat (setq I N)
    (repeat (setq J N) (setq L (cons (if (= I J) 1.0 0.0) l) J (1- J)))
    (setq M (cons L M) L nil I (1- I))
)
M
)

;; [功能] 返回矩阵的逆向矩阵
;;      使用高斯-若尔当消元法返回非奇异(可逆) nxn 矩阵的逆矩阵。
;; Matrix Inverse-gile & Lee Mac
;; Uses Gauss-Jordan Elimination to return the inverse of a non-singular nxn matrix.
;; Args: m - nxn matrix
(defun InvM (M / F C P R)
(defun F (P M)
    (mapcar '(lambda (X) (mapcar '(lambda (A B) (- A (* (car X) B))) (cdr X) P)) M)
)
(setq M (mapcar 'append M (IMat (length M))))
(while M
    (setq C (mapcar '(lambda (X) (abs (car X))) M))
    (repeat (vl-position (apply 'MAX C) C)
      (setq M (append (cdr M) (list (caR M))))
    )
    (if (equal 0. (caar M) 1E-14)
      (setq M nil R nil)
      (setq P (mapcar '(lambda ( x ) (/ (float X) (caar M))) (cdar M))
            M (F P (cdr M))
            R (cons P (F P R))
      )
    )
)
(reverse R)
)


;; [功能] 获取块定义到块参照的变换矩阵
;; RefGeom (gile)
;; Returns a list whose first item is a 3x3 transformation matrix and
;; second item the object insertion point in its parent (xref, block or space)
(defun RefGeom (ent / ang enx mat ocs)
(setq enx (entget ent)
      ang (cdr (assoc 050 enx))
      ocs (cdr (assoc 210 enx))
)
(list (setq mat (MxM (mapcar '(lambda (v) (trans v 0 ocs T))
                               '((1.0 0.0 0.0) (0.0 1.0 0.0) (0.0 0.0 1.0))
                     )
                     (MxM (list (list (cos ang) (- (sin ang)) 0.0)
                                  (list (sin ang) (cos ang) 0.0)
                                  '(0.0 0.0 1.0)
                            )
                            (list (list (cdr (assoc 41 enx)) 0.0 0.0)
                                  (list 0.0 (cdr (assoc 42 enx)) 0.0)
                                  (list 0.0 0.0 (cdr (assoc 43 enx)))
                            )
                     )
                  )
      )
      (mapcar '- (trans (cdr (assoc 10 enx)) ocs 0)
                   (MxV mat (cdr (assoc 10 (tblsearch "BLOCK" (cdr (assoc 2 enx))))))
      )
)
)

;; [功能] 获取块参照到块定义的变换矩阵
;; RevRefGeom (gile)
;; The inverse of RefGeom
(defun RevRefGeom (ent / ang enx mat ocs)
(setq enx (entget ent)
      ang (cdr (assoc 050 enx))
      ocs (cdr (assoc 210 enx))
)
(list (setq mat (MxM (list (list (/ 1.0 (cdr (assoc 41 enx))) 0.0 0.0)
                           (list 0.0 (/ 1.0 (cdr (assoc 42 enx))) 0.0)
                           (list 0.0 0.0 (/ 1.0 (cdr (assoc 43 enx))))
                     )
                     (MxM (list (list (cos ang) (sin ang) 0.0)
                                  (list (- (sin ang)) (cos ang) 0.0)
                                  '(0.0 0.0 1.0)
                            )
                            (mapcar '(lambda (v) (trans v ocs 0 T))
                                    '((1.0 0.0 0.0) (0.0 1.0 0.0) (0.0 0.0 1.0))
                            )
                     )
                  )
      )
      (mapcar '- (cdr (assoc 10 (tblsearch "BLOCK" (cdr (assoc 2 enx)))))
                   (MxV mat (trans (cdr (assoc 10 enx)) ocs 0))
      )
)
)


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

本帖最后由 你有种再说一遍 于 2025-12-11 18:21 编辑

都怪nentselp封装太狠了,没有正向过程导致逆向而行反而卡壳.
建议先学组块,这样才知道矩阵是什么.

caoyin 发表于 昨天 19:25

本帖最后由 caoyin 于 2025-12-11 19:41 编辑

方法2:
更直接的方法,只需要使用一次矩阵:

(defun C:TT2 (/ sel enx)
(setq sel (nentselp "\n选择套嵌块: ")
      enx (entget (last (last sel)))
)
(M4xP (trans (cdr (assoc 10 enx)) (cdr (assoc 210 enx)) 1)
      ;; 因为需要转换的点在块定义B内,直接获取块参照B的变换矩阵
      (apply 'LT:Tmatrix-3x4->4x4 (RevRefGeom (cadr (last sel))))
)
)

;;; TT2简化为如下:
;;; 使用3*4矩阵,计算量最小
(defun C:TT3 (/ sel enx)
(setq sel (nentselp "\n选择套嵌块: ")
      enx (entget (last (last sel)))
)
(apply 'M3xP
         (cons
         (trans (cdr (assoc 10 enx)) (cdr (assoc 210 enx)) 1)
         ;; 因为需要转换的点在块定义B内,直接获取块参照B的变换矩阵
         (RevRefGeom (cadr (last sel)))
         )
)
)

赘述:
其实AutoCAD内部的矩阵变换大多是仿射变换,使用3*3或3*4矩阵足够了,
而4*4矩阵的齐次变换的最后一行往往是(0 0 0 1)多了不少冗余计算。





llsheng_73 发表于 昨天 20:28

caoyin 发表于 2025-12-11 19:25
方法2:
更直接的方法,只需要使用一次矩阵:



看了大佬的帖子,感觉快明白变换矩阵了:loveliness:
页: [1]
查看完整版本: 关于矩阵变换的几个配套函数,高手请忽略