Rotate Multiple Blocks Around Their Center

Rotate Multiple Blocks Around Their Center

Anonymous
Not applicable
5,664 Views
28 Replies
Message 1 of 29

Rotate Multiple Blocks Around Their Center

Anonymous
Not applicable

Hello,

 

Does anyone know of a way to rotate multiple blocks around their centers instead of around their basepoint? I have a number of blocks, already on the drawing, with visible text attributes that I need to rotate all at once instead of individually rotating them.

 

For example, I have a block that is 20''W X 10''H. I would like the rotation to occur around its center, not the insertion point, which in this case would be located at 1/2 of the width and 1/2 of the height of the block. Reinserting the blocks would not be efficient as each block contains custom attributes entered manually.

 

Thank you.

0 Likes
5,665 Views
28 Replies
Replies (28)
Message 21 of 29

Kent1Cooper
Consultant
Consultant

@Anonymous wrote:

I found, through a few searches, the following:

 

(defun c:to nil (c: TORIENT)

 

Using defun c: should allow me to use the express tool command of choice.

....


I don't think so.  TORIENT itself is a (defun C:...) variety command, and those don't work in AutoLisp (command) functions, so neither will making another that simply calls that one.  You could still call it, with either (C:TO) or (C:TORIENT), but either way, the same problem I described earlier would remain -- AutoLisp can't feed it an answer to its new-orientation prompt.

 

But forget TORIENT -- I did, and just incorporated into the attached update the re-orienting of any Attributes that the Rotation causes to end up in the less-readable direction.

Kent Cooper, AIA
0 Likes
Message 22 of 29

john.uhden
Mentor
Mentor

I still have a once popular routine called HTEXT which would reorient Text and Mtext to visually horizontal after using ViewTwist.  Those myopic architects think nothing of rotating the world so it looks good in the camera.  We civils must rely on our coordinates, so we just rotate the camera.  It always changed the justification to Middle so that the objects didn't wander too far away from their intended position.  I could add attributes to its prey.  That would leave me in a slight turmoil.  Years ago using DCA and Softdesk, coordinate points were block insertions that had attributes for point #, elevation, and description.  They were each very close to the block insertion point.  With those we would just rotate the block insertion and DONE.  But I can see where a block has sort of sprawling attributes that they should be handled separately.  Does that sound like something that would help?  It would be written to handle a selection set of block references and reorient all the attributes attached.

John F. Uhden

0 Likes
Message 23 of 29

marko_ribar
Advisor
Advisor

This may work like TORIENT is implemented inside main routine... Note : you must have Express Tools installed...

 

;; Rotate Blocks at their Centres  -  Lee Mac ;;; mod by M.R. source link :
;; http://www.cadtutor.net/forum/showthread.php?99538-Insert-Block-at-selected-objects&p=677321&viewfull=1#post677321

(defun c:rotblkcen ( / *error* bln idx ref sel box att obj )

    (defun *error* ( msg )
        (LM:endundo (LM:acdoc))
        (if (not (wcmatch (strcase msg t) "*break,*cancel*,*exit*"))
            (princ (strcat "\nError: " msg))
        )
        (princ)
    )

    (LM:startundo (LM:acdoc))
    (if (and (setq sel (LM:ssget (strcat "\nSelect blocks to rotate at their centres: ") '(((0 . "INSERT")))))
            ;; Kent Cooper input from another code pasted by M.R.
             (not (initget 2)); no zero
             (setq *ang
               (cond
                 ( (getangle
                     ; [can type in current angle units format, or pick two points;
                     ; returns in radians, nil on Enter]
                     (strcat
                       "\nAngle to Rotate each Block <"
                       (if *ang (angtos *ang) "180")
                         ; offer prior value if present as default, otherwise 180
                       ">: "
                     ); strcat
                   ); getangle
                 ); User-input [other than Enter] condition
                 (*ang); on Enter with prior value
                 (pi); on Enter without prior value
               ); cond
             ); setq
            ;; end of input
        )
        (repeat (setq idx (sslength sel))
            (setq bln (cdr (assoc 2 (entget (ssname sel (setq idx (1- idx)))))))
            (if (and
                  (not ; not Xref [no path in Block table listing]
                    (assoc 1
                      (tblsearch "block" bln)
                    ); assoc
                  ); not
                  (setq ref (vlax-ename->vla-object (ssname sel idx))
                        box (LM:blockreferenceboundingbox ref)
                  )
                )
                (progn
                  (vla-rotate ref
                      (vlax-3D-point (mapcar '/ (apply 'mapcar (cons '+ box)) '(4.0 4.0)))
                      *ang
                  )
                  ;; Kent Cooper input from another code pasted by M.R.
                  (setq att (vlax-vla-object->ename ref)); starting point for orienting Attributes
                  (while ; re-orient Attributes if appropriate
                    (and
                      (setq att (entnext att))
                      (setq obj (vlax-ename->vla-object att))
                      (= (vla-get-ObjectName obj) "AcDbAttribute"); not end yet
                    ); and
                    (if
                      (and ; in less-readable direction
                        (> (vla-get-Rotation obj) (/ pi 2))
                        (<= (vla-get-Rotation obj) (* pi 1.5))
                      ); and
                      (vla-rotate obj 
                          (vlax-3d-point (mapcar '/ (apply 'mapcar (cons '+ (acet-geom-textbox (entget att) 0.0))) '(4.0 4.0)))
                          pi); then -- turn around
                    ); if
                  ); while
                  ;; end of input
                )
            )
        )
    )
    (LM:endundo (LM:acdoc))
    (princ)
)

;; ssget  -  Lee Mac
;; A wrapper for the ssget function to permit the use of a custom selection prompt
;; msg - [str] selection prompt
;; arg - [lst] list of ssget arguments

(defun LM:ssget ( msg arg / sel )
    (princ msg)
    (setvar 'nomutt 1)
    (setq sel (vl-catch-all-apply 'ssget arg))
    (setvar 'nomutt 0)
    (if (not (vl-catch-all-error-p sel)) sel)
)

;; Block Reference Bounding Box  -  Lee Mac
;; Returns a WCS point list describing a rectangular frame bounding all geometry of a supplied block reference.
;; Excludes Text, MText & Attribute Definitions.
;; ref - [vla] Block Reference Object

(defun LM:blockreferenceboundingbox ( ref )
    (
        (lambda ( lst )
            (apply
                (function
                    (lambda ( m v )
                        (mapcar (function (lambda ( p ) (mapcar '+ (mxv m p) v))) lst)
                    )
                )
                (refgeom (vlax-vla-object->ename ref))
            )
        )
        (LM:blockdefinitionboundingbox
            (vla-item
                (vla-get-blocks (vla-get-document ref))
                (vla-get-name ref)
            )
        )
    )
)

;; Block Definition Bounding Box  -  Lee Mac
;; Returns a WCS point list describing a rectangular frame bounding all geometry of a supplied block definition.
;; Excludes Text, MText & Attribute Definitions.
;; def - [vla] Block Definition Object

(defun LM:blockdefinitionboundingbox ( def / llp lst urp )
    (vlax-for obj def
        (cond
            (   (= :vlax-false (vla-get-visible obj)))
            (   (= "AcDbBlockReference" (vla-get-objectname obj))
                (setq lst (append lst (LM:blockreferenceboundingbox obj)))
            )
            (   (and (not (wcmatch (vla-get-objectname obj) "AcDbAttributeDefinition,AcDb*Text"))
                     (vlax-method-applicable-p obj 'getboundingbox)
                     (not (vl-catch-all-error-p (vl-catch-all-apply 'vla-getboundingbox (list obj 'llp 'urp))))
                )
                (setq lst (vl-list* (vlax-safearray->list llp) (vlax-safearray->list urp) lst))
            )
        )
    )
    (LM:points->boundingbox lst)
)

;; Points to Bounding Box  -  Lee Mac
;; Returns the rectangular extents of a supplied point list

(defun LM:points->boundingbox ( lst )
    (   (lambda ( l )
            (mapcar '(lambda ( a ) (mapcar '(lambda ( b ) ((eval b) l)) a))
               '(
                    (caar   cadar)
                    (caadr  cadar)
                    (caadr cadadr)
                    (caar  cadadr)
                )
            )
        )
        (mapcar '(lambda ( f ) (apply 'mapcar (cons f lst))) '(min max))
    )
)

;; RefGeom (gile)
;; Returns a list which first item is a 3x3 transformation matrix (rotation, scales, normal)
;; and second item the object insertion point in its parent (xref, block or space)
;; Argument : an ename

(defun refgeom ( ent / ang ang 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))))))
        )
    )
)

;; Matrix x Vector - Vladimir Nesterovsky
;; Args: m - nxn matrix, v - vector in R^n

(defun mxv ( m v )
    (mapcar '(lambda ( r ) (apply '+ (mapcar '* r v))) m)
)

;; Matrix Transpose - Doug Wilson
;; Args: m - nxn matrix

(defun trp ( m )
    (apply 'mapcar (cons 'list m))
)

;; Matrix x Matrix - Vladimir Nesterovsky
;; Args: m,n - nxn matrices

(defun mxm ( m n )
    ((lambda ( a ) (mapcar '(lambda ( r ) (mxv a r)) m)) (trp n))
)

;; Start Undo  -  Lee Mac
;; Opens an Undo Group.

(defun LM:startundo ( doc )
    (LM:endundo doc)
    (vla-startundomark doc)
)

;; End Undo  -  Lee Mac
;; Closes an Undo Group.

(defun LM:endundo ( doc )
    (while (= 8 (logand 8 (getvar 'undoctl)))
        (vla-endundomark doc)
    )
)

;; Active Document  -  Lee Mac
;; Returns the VLA Active Document Object

(defun LM:acdoc nil
    (eval (list 'defun 'LM:acdoc 'nil (vla-get-activedocument (vlax-get-acad-object))))
    (LM:acdoc)
)

(vl-load-com) (princ)

P.S. It should find centers of blocks according to enames that reside in block and aren't TEXTs, MTEXTs or ATTRIBUTEs...

 

HTH., M.R.

(If you are satisfied with post, mark it as solution...)

Marko Ribar, d.i.a. (graduated engineer of architecture)
Message 24 of 29

Anonymous
Not applicable

Thanks for the help!

 

I would like to set the default rotation to 180 degrees.In it's current state, when the user inputs an angle other than 180 degrees, the next time the function is run it doesn't revert back to 180 but to the users last angle they input. I would like it to revert back to 180 every time. Do you know how to make that modification?

 

(LM:startundo (LM:acdoc))
    (if (and (setq sel (LM:ssget (strcat "\nSelect blocks to rotate at their centres: ") '(((0 . "INSERT")))))
            ;; Kent Cooper input from another code pasted by M.R.
             (not (initget 2)); no zero
             (setq *ang
               (cond
                 ( (getangle
                     ; [can type in current angle units format, or pick two points;
                     ; returns in radians, nil on Enter]
                     (strcat
                       "\nAngle to Rotate each Block <"
                       (if *ang (angtos *ang) "180")
                         ; offer prior value if present as default, otherwise 180
                       ">: "
                     ); strcat
                   ); getangle
                 ); User-input [other than Enter] condition
                 (*ang); on Enter with prior value
                 (pi); on Enter without prior value
               ); cond
             ); setq
            ;; end of input

 

0 Likes
Message 25 of 29

Kent1Cooper
Consultant
Consultant

@Anonymous wrote:

.... 

I would like to set the default rotation to 180 degrees.In it's current state, when the user inputs an angle other than 180 degrees, the next time the function is run it doesn't revert back to 180 but to the users last angle they input. I would like it to revert back to 180 every time. Do you know how to make that modification?


That's far easier than having it save and offer their previous value:

             (setq ang
               (cond
                 ( (getangle
                     ; [can type in current angle units format, or pick two points;
                     ; returns in radians, nil on Enter]
                     "\nAngle to Rotate each Block <180>: "
                   ); getangle
                 ); User-input [other than Enter] condition
                 (pi); on Enter
               ); cond
             ); setq

 

Also, I took the asterisk off the beginning of *ang as a variable name [just my little way of indicating a global variable, which it wouldn't be any more], and you should add ang to the localized variables list at the beginning of the definition.

Kent Cooper, AIA
0 Likes
Message 26 of 29

Anonymous
Not applicable

I added "ang" to the list of variables at the beginning as such:

 

(defun c:rotblkcen ( / *error* ang bln idx ref sel box att obj )

 

The function now rotates the block 90 degrees instead of 180 degrees.

 

I can't figure out why as the function should rotate it by pi (180 degrees)
 

0 Likes
Message 27 of 29

Kent1Cooper
Consultant
Consultant

@Anonymous wrote:

.... 

The function now rotates the block 90 degrees instead of 180 degrees.

 

I can't figure out why as the function should rotate it by pi (180 degrees)
 


If you're otherwise in the code from Post 23, there's another instance of *ang further down that would need its asterisk removed.  You probably have an old 90-degree value of it with the asterisk floating around from earlier trials.

Kent Cooper, AIA
0 Likes
Message 28 of 29

marko_ribar
Advisor
Advisor

Just FYI :

 

Above posted code had a small lack that refers to using lisp in rotated UCS rather then WCS :

Find line with (vla-rotate obj ... pi) and replace it with :

(vla-rotate

  obj
  (vlax-3d-point (trans (mapcar '/ (apply 'mapcar (cons '+ (acet-geom-textbox (entget att) 0.0))) '(4.0 4.0)) 1 0))
  pi

); then -- turn around

 

If you solved the issue with angles (default = always 180), then you forgot to mark this topic as solved...

Post your solution if you find it appropriate and mark it as solution, or mark the reply you think that responds to solution as solution...

 

This is my humble opinion but Lee Mac is the one you should give credits to... I and Kent only minimally added appropriate coding examples, although Kent dived deeply to answer your question and he should be awarded too...

 

M.R.

Marko Ribar, d.i.a. (graduated engineer of architecture)
0 Likes
Message 29 of 29

Anonymous
Not applicable

This is exactly what I have been after. 

Rotates Multiple Blocks from the Geometric Centre of the outer most Rectangle. 

Many thanks. 


@Kent1Cooper wrote:

@Anonymous wrote:

Thanks Dennis,

 

I have a number of blocks whose attributes are above or below the block which skews the actually center of the block. Is there a way to find the center of the polyline block/rectangle only and use that as the center?


Bringing this over from the other thread on the same subject -- always better to avoid duplications, so if anyone else has the same question, they won't miss the solution on one thread if they happen to find the question on the thread that doesn't have it....

 

Try the attached.  Lightly tested, but it seems to work.  Read the comments at the top of the file -- it's quite specific to the Block configuration described on the other thread.


 

0 Likes