Jump to content

Matrix Transformation in Xref


Jonathan Handojo

Recommended Posts

Hi guys,

 

I'm stumbling on a problem with xrefs…

 

The case I'm having is that one of my colleagues will work off a drawing placing some "unique" blocks on his drawing. Once it's done, I attach the xref of his drawing, and I place some corresponding blocks (let's just assume one specific block for this forum) on top of his unique blocks (exactly on the insertion point of the xref blocks) by simply clicking on his xref. Sure I was successful enough by selecting the xref through nentsel and running vlax-for like below to extract only the specific (unique) blocks I need: 

  (vlax-for x (vla-item (vla-get-blocks adoc) (vla-get-EffectiveName vxref))
    (if
      (and
	(eq (vla-get-ObjectName x) "AcDbBlockReference")
	(eq (vla-get-IsDynamicBlock x) :vlax-true)
	(setq atts (vlax-invoke x 'GetAttributes))
	(vl-some
	  '(lambda (x)
	     (and
	       (eq (vla-get-TagString x) "TAG1")
	       (wcmatch (strcase (vla-get-TextString x)) "somevalues")
	       )
	     )
	  atts
	  )
	)
      (setq objs (cons x objs))
      )
    )

 

My case is that the xref drawn is not "on-the-spot" with my drawing, so I had to use the ALIGN command where the xref is moved, rotated, and scaled to match my drawing. So in order to align the points, I used the third element from the nentsel function along with the function I did below:

 

;; JH:pt-matrix --> Jonathan Handojo
;; Transforms a point through matrix
;; pt - point to transform
;; mtx - a 3x3 matrix transformation value

(defun JH:pt-matrix (pt mtx)
  (mapcar '(lambda (x / np)
	     (setq np
		    (apply '+
			   (mapcar '*
				   (mapcar 'car mtx)
				   pt
				   )
			   )
		   mtx (mapcar 'cdr mtx)
		   )
	     np
	     )
	  mtx
	  )
  )

I run my function above using the first three elements of the matrix formed by nentsel, and the blocks which I wish to insert aligns perfectly in terms of rotation and scale. But it's not inserted exactly on the location and I'd have to do one extra MOVE command to place all of them on the spot.

 

I'm quite confused on how to make the blocks appear on the spot.

Link to comment
Share on other sites

The 4th row of the matrix returned by nentsel is the position.  The first three rows define the orientation and scale.  You can move the block to the position defined by the 4th row or add a 4th element to the point coordinate (a 1) , and then multiply it against the  4 x 3 transformation matrix. 

 

Here's a function I wrote that multiplies a point and 4 x 3 matrix  to yield a point in the coordinate system defined by the matrix.  I'm sure there's a more elegant way to code this.  I wrote this before knowing about the trans function which you could probably use instead. 

(defun transform (a HTM / transpt)	;Coordinate transformation via matrix multiplication
					; a = point (3 coordinates)
					; HTM = homogeneous transformation matrix (3 x 4)
(setq aH (append a '(1.0)))		; aH = homogeneous point in local coordinates (1 x 4)
(setq	transpt				
	 (list				
	   (+				; transpt = list of x, y, z coordinates
	     (* (nth 0 aH) (nth 0 (nth 0 HTM)))
	     (* (nth 1 aH) (nth 0 (nth 1 HTM)))
	     (* (nth 2 aH) (nth 0 (nth 2 HTM)))
	     (* (nth 3 aH) (nth 0 (nth 3 HTM)))
	   )
	   (+
	     (* (nth 0 aH) (nth 1 (nth 0 HTM)))
	     (* (nth 1 aH) (nth 1 (nth 1 HTM)))
	     (* (nth 2 aH) (nth 1 (nth 2 HTM)))
	     (* (nth 3 aH) (nth 1 (nth 3 HTM)))
	   )
	   (+
	     (* (nth 0 aH) (nth 2 (nth 0 HTM)))
	     (* (nth 1 aH) (nth 2 (nth 1 HTM)))
	     (* (nth 2 aH) (nth 2 (nth 2 HTM)))
	     (* (nth 3 aH) (nth 2 (nth 3 HTM)))
	   )
	 )
  )
(setq transpt (list (nth 0 transpt) (nth 1 transpt) (nth 2 transpt) ))  
) ;end transform 

 

 

 
 
Link to comment
Share on other sites

Firstly, it is worth noting that there is a difference in the structure of the transformation matrices returned by the nentsel & nentselp functions: where the nentselp function returns a standard 4x4 transformation matrix, as might be used by the ActiveX transformby method and vlax-tmatrix function, the nentsel function returns a 4x3 matrix which, if appended with a column vector (0 0 0 1) to form a 4x4 matrix, represents the transposition of the matrix returned by nentselp. As such, I always opt to use the nentselp function so that the transformation matrix is consistent with those used elsewhere in the LISP and ObjectARX APIs.

 

The easiest way to transform your blocks would be to use the ActiveX transformby method, supplied with a transformation matrix generated from the 4x4 matrix returned by the nentselp function; though, note that such a transformation is limited to orthogonal matrices, and therefore cannot handle non-uniformly-scaled blocks.

 

Here is a quick example using this method:

(defun c:test1 ( / blk sel )
    (setq blk "Block1")
    (cond
        (   (not (tblsearch "block" blk))
            (princ (strcat "\n" blk " not found in the active drawing."))
        )
        (   (not (setq sel (nentselp "\nSelect nested block: "))))
        (   (/= 4 (length sel))
            (princ "\nSelected object is not nested.")
        )
        (   (vla-transformby
                (vla-insertblock
                    (vlax-get-property
                        (vla-get-activedocument
                            (vlax-get-acad-object)
                        )
                        (if (= 1 (getvar 'cvport))
                            'paperspace
                            'modelspace
                        )
                    )
                    (vlax-3D-point 0 0)
                    blk
                    1.0 1.0 1.0 0.0
                )
                (vlax-tmatrix (caddr sel))
            )
        )
    )
    (princ)
)

 

  • Like 1
Link to comment
Share on other sites

FWIW, my existing Extract Nested Block program is performing the same operation as you have described, only deleting the original nested block within the block definition - removing the deletion operation should yield the result you require (you will also need to tweak the code to allow selection of xrefs).

Edited by Lee Mac
Link to comment
Share on other sites

Thanks Lee, nentselp certainly got the things going. But it's kinda funny that the matrix returned by nentselp can vary depending on the item you choose. Since I'm dealing with an xref, there are many other objects in there, in which (when using nentselp) returns the same transformation matrix, but a different fourth row depending on what's selected (even though it's the same exact xref selected), therefore interfering with my desired result.

 

Through nentselp, it got the appropriate transformation matrix, but I had to change the fourth row like this:

 

 (mapcar '(lambda (x y)
	    (subst
	      y
	      (last x)
	      x
	      )
	    )
	 (caddr xref)
	 (append (cdr (assoc 10 (entget (last (last xref))))) '(1.0))
	 )

 

Not sure if it's the right way to do it, but at least this way I got all the blocks to insert right.

Edited by Jonathan Handojo
Link to comment
Share on other sites

48 minutes ago, lrm said:

The 4th row of the matrix returned by nentsel is the position.  The first three rows define the orientation and scale.  You can move the block to the position defined by the 4th row or add a 4th element to the point coordinate (a 1) , and then multiply it against the  4 x 3 transformation matrix. 

 

Here's a function I wrote that multiplies a point and 4 x 3 matrix  to yield a point in the coordinate system defined by the matrix.  I'm sure there's a more elegant way to code this.  I wrote this before knowing about the trans function which you could probably use instead. 


(defun transform (a HTM / transpt)	;Coordinate transformation via matrix multiplication
					; a = point (3 coordinates)
					; HTM = homogeneous transformation matrix (3 x 4)
(setq aH (append a '(1.0)))		; aH = homogeneous point in local coordinates (1 x 4)
(setq	transpt				
	 (list				
	   (+				; transpt = list of x, y, z coordinates
	     (* (nth 0 aH) (nth 0 (nth 0 HTM)))
	     (* (nth 1 aH) (nth 0 (nth 1 HTM)))
	     (* (nth 2 aH) (nth 0 (nth 2 HTM)))
	     (* (nth 3 aH) (nth 0 (nth 3 HTM)))
	   )
	   (+
	     (* (nth 0 aH) (nth 1 (nth 0 HTM)))
	     (* (nth 1 aH) (nth 1 (nth 1 HTM)))
	     (* (nth 2 aH) (nth 1 (nth 2 HTM)))
	     (* (nth 3 aH) (nth 1 (nth 3 HTM)))
	   )
	   (+
	     (* (nth 0 aH) (nth 2 (nth 0 HTM)))
	     (* (nth 1 aH) (nth 2 (nth 1 HTM)))
	     (* (nth 2 aH) (nth 2 (nth 2 HTM)))
	     (* (nth 3 aH) (nth 2 (nth 3 HTM)))
	   )
	 )
  )
(setq transpt (list (nth 0 transpt) (nth 1 transpt) (nth 2 transpt) ))  
) ;end transform 

 

 

 
 

With some apply and mapcar, that could be accomplished. But you'll need another UCS for trans, and I don't think I'd want to do that just to get my transformation right.

Simply put, I'm only looking to insert (let's just say one block for this forum, although in actual, there's many others) in the points within an xref that has been moved, rotated, and scaled through the ALIGN command. Then I searched around the web and got an idea for matrix transformation that can align your points. Thanks for the function. Now I know it's the result after rotation and scaling, but not movement.

Link to comment
Share on other sites

4 hours ago, Jonathan Handojo said:

it's kinda funny that the matrix returned by nentselp can vary depending on the item you choose. Since I'm dealing with an xref, there are many other objects in there, in which (when using nentselp) returns the same transformation matrix, but a different fourth row depending on what's selected (even though it's the same exact xref selected), therefore interfering with my desired result.

 

If you're using nentselp, the fourth row in the transformation matrix should always be the vector '(0 0 0 1), since the purpose of this row is purely to yield a square matrix - perhaps you were looking at the output from nentsel, for which the fourth row is the translation vector?

 

The matrix returned by nentselp will certainly vary depending on the object selected, since nentselp will select the innermost nested object within a nested block hierarchy, and the resulting transformation matrix will be the result of tranforming the object relative to the OCS of each block in such hierarchy.

Link to comment
Share on other sites

1 hour ago, Lee Mac said:

 

If you're using nentselp, the fourth row in the transformation matrix should always be the vector '(0 0 0 1), since the purpose of this row is purely to yield a square matrix - perhaps you were looking at the output from nentsel, for which the fourth row is the translation vector?

 

The matrix returned by nentselp will certainly vary depending on the object selected, since nentselp will select the innermost nested object within a nested block hierarchy, and the resulting transformation matrix will be the result of tranforming the object relative to the OCS of each block in such hierarchy.

Perhaps I mistook "row" for "column" because I think that's how I've learned about matrices in high school (can't remember, but I do know how to multiply them -- fortunately) ... I simply meant the last element in each of the four matrices like if you do (mapcar 'last (caddr blk)) on nentselp. When I clicked on the xref, the stated value is not always the same, thus when I do vla-transformby following the matrix from nentselp, the result isn't consistent, and the block is not inserted in the right location, so I had to change that manually using the code I wrote above.

 

Supposedly I clicked on a table within that xref, and then I clicked on something else (say, a line)... The 4×3 matrix is the same as I noticed, but the fourth column can vary. Both of them are not nested within another block, they're simply pure tables and lines in the xref drawing, so yea, I'm somewhat confused.

 

Your explanations are definitely giving me more and more knowledge and I thank you for that.

Edited by Jonathan Handojo
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...