Rotate Multiple Blocks Around Their Center

Rotate Multiple Blocks Around Their Center

Anonymous
Not applicable
5,629 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,630 Views
28 Replies
Replies (28)
Message 2 of 29

dennis
Advisor
Advisor

To get you going, start with getting the Bounding Box of each block, then calculate the 'center' based on that bounding box.  Then rotate.  Below is some Lee Mac code that will help you along the way.  The 'gotcha' that I can see could happen, if an attribute has been moved a ways from the block itself, then the center point calculated could be distorted by that much as well.

 

(defun LM:SSBoundingBox ( ss / bb )
  (vl-load-com)
  ;; © Lee Mac 2010
 
  (
    (lambda ( i / e ll ur )
      (while (setq e (ssname ss (setq i (1+ i))))
        (vla-getBoundingBox (vlax-ename->vla-object e) 'll 'ur)

        (setq bb (cons (vlax-safearray->list ur)
                       (cons (vlax-safearray->list ll) bb))
        )
      )
    )
    -1
  )
  (
    (lambda ( data )
      (mapcar
        (function
          (lambda ( funcs )
            (mapcar
              (function
                (lambda ( func ) ((eval func) data))
              )
              funcs
            )
          )
        )
       '((caar cadar) (caadr cadar) (caadr cadadr) (caar cadadr))
      )
    )
    (mapcar
      (function
        (lambda ( operation )
          (apply (function mapcar) (cons operation bb))
        )
      )
     '(min max)
    )
  )
)



(defun c:test ( / ss )
  (vl-load-com)

  (if (setq ss (ssget))
    (vla-put-closed
      (vlax-invoke
        (vlax-get-property (vla-get-ActiveDocument (vlax-get-acad-object))
          (if (= 1 (getvar 'CVPORT)) 'Paperspace 'Modelspace)
        )
        'AddLightWeightPolyline     
        (apply (function append) (LM:SSBoundingBox ss))
      )
      :vlax-true
    )
  )

  (princ)
)

Message 3 of 29

Anonymous
Not applicable

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?

0 Likes
Message 4 of 29

braudpat
Mentor
Mentor

 

Hello from France

 

Please find attached a French routine :  BROT  &  BSCA

which rotate / scale all selected Blocks relatively to their Insertion Point ...

 

Thanks to Gilles (gile) !

 

Regards, Patrice

 

 

 

 

Patrice ( Supporting Troops ) - Autodesk Expert Elite
If you are happy with my answer please mark "Accept as Solution" and if very happy please give me a Kudos (Felicitations) - Thanks

Patrice BRAUD

EESignature


Message 5 of 29

Kent1Cooper
Consultant
Consultant

@braudpat wrote:

.... 

Please find attached a French routine :  BROT  &  BSCA

which rotate / scale all selected Blocks relatively to their Insertion Point ...

.... 


[Re-read the initial question -- not about their insertion points.]

Kent Cooper, AIA
0 Likes
Message 6 of 29

Kent1Cooper
Consultant
Consultant

@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.

Kent Cooper, AIA
Message 7 of 29

braudpat
Mentor
Mentor

 

Hello Kent & mk832b

 

1) I am VERY SORRY !

 

2) As a "Poor Froggy" Man, I read too quickly your Topic !

 

3) So if I meet you, I will offer you a Beer or Whiskey ...

 

Regards, Patrice

 

Patrice ( Supporting Troops ) - Autodesk Expert Elite
If you are happy with my answer please mark "Accept as Solution" and if very happy please give me a Kudos (Felicitations) - Thanks

Patrice BRAUD

EESignature


Message 8 of 29

MatTheDrafter
Advocate
Advocate

Are you referring to their geometric centers?

0 Likes
Message 9 of 29

Kent1Cooper
Consultant
Consultant

@BrutalDrafter wrote:

Are you referring to their geometric centers?


See the second link [under the word "described"] in my previous Reply, my Post 7 there that it's in response to, and that thread generally.

Kent Cooper, AIA
0 Likes
Message 10 of 29

Anonymous
Not applicable

Thanks Kent, this works quite well.

 

I am attempting to assign "180" as the desired angle by default for the block rotation by using this code modification:

 

(setq ang 180) ;;;;; remember/offer default?

 

When I do that, the block doesn't fully rotate 180 degrees but instead rotates approximately 135 degrees. Do you know what could be causing that?

 

Also, after the block is finished rotating, I would like to use the "torient" command to fix the orientation of the text for the blocks that were rotated. Could you tell me where I should enter that command?

 

Thank you.

0 Likes
Message 11 of 29

Kent1Cooper
Consultant
Consultant

@Anonymous wrote:

... 

I am attempting to assign "180" as the desired angle by default for the block rotation by using this code modification:

 

(setq ang 180) ;;;;; remember/offer default?

 

When I do that, the block doesn't fully rotate 180 degrees but instead rotates approximately 135 degrees. Do you know what could be causing that?

....


These things work in radians.  Try (setq ang pi).

Kent Cooper, AIA
Message 12 of 29

Anonymous
Not applicable

As you can tell I am new to LISP. Changing it to "pi" worked as expected.

 

Do you know how to incorporate the "TORIENT' command after the blocks are rotated? I'm receiving an "Unknown command "TORIENT".  Press F1 for help." error when I include this line of code after the block rotate portion of code:

 

(command "TORIENT" "")

 

I tried to select all the blocks on the drawing as well with the "AI_SELALL" command and that wasn't recognized as well with this modification to the line of code above:

 

(command "AI_SELALL" "TORIENT" "")

0 Likes
Message 13 of 29

Kent1Cooper
Consultant
Consultant

@Anonymous wrote:

.... after the block is finished rotating, I would like to use the "torient" command to fix the orientation of the text for the blocks that were rotated. Could you tell me where I should enter that command?

....


Unfortunately, TORIENT is not a native AutoCAD command, but an Express Tool, so it can't be used in a (command) function.  Non-native commands defined as C:WHATEVER can be used in AutoLisp routines by calling their names with the C: prefix in parentheses, and can be imposed on objects if they're pre-selected and if there are no further inputs or options to specify.  That's not true of this Tool -- I can get as far as starting it for the rotated Block, by adding this after the Rotate (command) function:

 

(sssetfirst nil (ssadd blk)); pre-select/grip/highlight the Block

(C:TORIENT); run it on that Block

 

But it leaves it at the "New absolute rotation <Most Readable>:" prompt, and I don't know of a way for the routine to give it an answer to that.  Following that with (command "") would feed in an Enter for a native AutoCAD command, but doesn't work in this situation.

 

I'm not well-versed in such intricacies -- maybe someone else knows whether there's some way to do that.  Short of that, it may be necessary to incorporate whatever TORIENT does into the routine as additional code, rather than through the Express Tool itself.  Or, the routine could wait until the bitter end and run TORIENT on all the selected Blocks together, in which case you would still need to supply a manual answer to that prompt for all of them, but only one.

Kent Cooper, AIA
0 Likes
Message 14 of 29

john.uhden
Mentor
Mentor

Maybe using (vlax-add-cmd) will create a true AutoCAD command that can be fed the input, scriptly speaking.

 

Then again, I always wonder why users get themselves into these problems, as in why wasn't the block inserted at the desired rotation in the first place?

Of course that probably would orient the attributes at the same odd rotation as well.  Sounds like what he really needs is a function to re-orient the attributes.

 

I noticed in the OSNAP thread that OSMODE bit 1024 is now for geometric center, whatever that is.  I'm still running 2002.  Might that (osnap) solve his origin problem?

 

If not, then he could use my recently posted ATTDEL to temporarily remove the attributes, then use getboundingbox, then undo to get back the attributes, etc.

 

Ever wonder why old farts just babble on?  I think they do just because they still can.  When I'm gone from here, I'm gone.

John F. Uhden

0 Likes
Message 15 of 29

Kent1Cooper
Consultant
Consultant

@Anonymous wrote:

.... 

I am attempting to assign "180" as the desired angle by default for the block rotation ....


Since "default" [to me] implies a value that is offered, that the User can accept with Enter but can also replace with a different value if desired, which will then be remembered and offered on subsequent use, the change you have made doesn't really constitute a "default" as I understand the concept, but a permanent, built in, constant rotation angle value.  The attached enhanced version incorporates a "real" default, with 180 degrees being offered as the initial default [on first use in a given editing session], and it remembers whatever value was most recently used [whether entered explicitly or a default accepted with Enter] to offer again next time.

Kent Cooper, AIA
0 Likes
Message 16 of 29

marko_ribar
Advisor
Advisor

Please check this example by Lee Mac... He stated in supplied sub functions that texts or attributes are excluded from bounding box centers calculations...

I don't have a time to dive into your request for rotation, but I believe that this is close to what you're searching for for the first step... For TORIENT I don't have appropriate suggestion yet...

 

http://www.cadtutor.net/forum/showthread.php?99538-Insert-Block-at-selected-objects&p=677321&viewful...

 

HTH., M.R.

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

Kent1Cooper
Consultant
Consultant

@Kent1Cooper wrote:

...

it may be necessary to incorporate whatever TORIENT does into the routine as additional code, rather than through the Express Tool itself.  ....


I find that the command definition is available, as part of ACETTXT.lsp, which is in the Acad20xx folder structure.  It involves a lot more than necessary here, because it's made to work with Text/Mtext/Dimensions as well as Attributes [all this routine needs to deal with].  And it calls on several sub-routines, some of which are also defined in that file but others elsewhere and apparently loaded in with the Express Tools collectively.  Some contemplation of the process it uses just for orienting Attributes may lead to a way of incorporating similar function into this routine without all the overhead.
But assuming figuring that out, would you want to have the option for either an explicit rotation or the more-readable version of an Attribute's basic rotation in relation to the Block, as TORIENT has?  Or would you always want one or the other of those choices, so that it can be built in and not bother asking the User?

 

Kent Cooper, AIA
0 Likes
Message 18 of 29

Anonymous
Not applicable

My intent is to use TORIENT with the MOST READABLE version. It would be best to limit the amount of input from the user so the MOST READABLE option would be built into the LSP code.

0 Likes
Message 19 of 29

marko_ribar
Advisor
Advisor

In case you can't get with my offered link, here is my mod of it...

 

 

;; 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* box idx ref sel )

    (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))
            (if (setq ref (vlax-ename->vla-object (ssname sel (setq idx (1- idx))))
                      box (LM:blockreferenceboundingbox ref)
                )
                (vla-rotate ref
                    (vlax-3D-point (mapcar '/ (apply 'mapcar (cons '+ box)) '(4.0 4.0)))
                    *ang
                )
            )
        )
    )
    (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)
)

;; Select if Object  -  Lee Mac
;; Continuously prompts the user for a selection of a specific object type

(defun LM:selectifobject ( msg typ / ent )
    (while
        (progn (setvar 'errno 0) (setq ent (car (entsel msg)))
            (cond
                (   (= 7 (getvar 'errno))
                    (princ "\nMissed, try again.")
                )
                (   (null ent) nil)
                (   (not (wcmatch (cdr (assoc 0 (entget ent))) typ))
                    (princ "\nInvalid object selected.")
                )
            )
        )
    )
    ent
)

;; 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)

 

Hope this helps... M.R.

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

Anonymous
Not applicable

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 would include:

 

(defun c:to nil (c:ai_selall)

 

Ths will select all the blocks on the floor prior to executing the TORIENT command.

 

Could you tell me where I would place the 2 lines in your code? I tried after:

 

(command "_.rotate" blk ""
              (polar ; rotation base point
                (bval 10)
                (+ (bval 50) (cadr (assoc blkname blklist)))
                (* (bval 41) (caddr (assoc blkname blklist)))
              ); polar
              (angtos ang (getvar 'aunits) 8)
            ); command

 

But this skipped over my code and also gave no errors.

 

 

0 Likes