Rotate Attributes just like rotate command

Rotate Attributes just like rotate command

thomas_schluesselberger
Advocate Advocate
7,639 Views
38 Replies
Message 1 of 39

Rotate Attributes just like rotate command

thomas_schluesselberger
Advocate
Advocate

Hi,

 

i'm searching for a LISP which lets me rotate attributes just like i would rotate texts with the rotate command (one select basepoint where they get rotated around).

The LISP should work if i already have attributes selected (with CTRL+leftclick) and also if i dont have selected attributes, the lisp should ask me for an selection. It would be nice if the lisp is also able to rotate attributes and normal texts at one time.

 

Currently i need to change the rotation via "torient" or by changing the rotation of each attribute, which takes much time because i have to re-arrange the attributes each time.

 

Maybe someone can help 🙂

0 Likes
Accepted solutions (1)
7,640 Views
38 Replies
Replies (38)
Message 2 of 39

ВeekeeCZ
Consultant
Consultant

Somehow don't understand why you use att sub-selection for TORIENT. 

0 Likes
Message 3 of 39

thomas_schluesselberger
Advocate
Advocate

I don't.

 

"Currently i need to change the rotation via "torient" OR by changing the rotation of each attribute"

0 Likes
Message 4 of 39

komondormrex
Mentor
Mentor

hey,

while attribute is selectable entity, you may rotate it using this

(vla-rotate (vlax-ename->vla-object (car (nentsel "\nPick an attribute"))) (vlax-3d-point (setq center_point (getpoint "\nPick rotation center: "))) (getangle center_point "\nSelect rotation angle: "))
0 Likes
Message 5 of 39

thomas_schluesselberger
Advocate
Advocate

Hi.

 

Thanks for your answer!

 

Is it possible to change it, that i can rotate multible attributes?

And it would be very very helpfull if i have a live preview of the attributes i rotate, just like when you normaly rotate objects/texts.

 

0 Likes
Message 6 of 39

Moshe-A
Mentor
Mentor

@thomas_schluesselberger  hi,

 

why TORIENT is not suitable?

 

Moshe

 

 

 

0 Likes
Message 7 of 39

thomas_schluesselberger
Advocate
Advocate

Hi Moshe!

 

I attached you 2 videos where you see my problem.

 

Torient.mp4: Here you see what my problem is. When i use torient the attributes rotate around there individual basepoints. And then i have to position my attributes again.

 

Rotate.mp4: I bursted the block to show how i would like the attributes to behave.

 

Hopefully now it is easier to understand, what i tried to say 😅

 

0 Likes
Message 8 of 39

Moshe-A
Mentor
Mentor

have you thought making it dynamic block + rotate action?

 

 

 

0 Likes
Message 9 of 39

thomas_schluesselberger
Advocate
Advocate

Yes, but the problem with this is, that i have like 100 of different blocks and even if i change all my blocks i think it is only possible to rotate attributes with an rotate parameter if there positions are locked -> but thats not possible in my case.

 

 

0 Likes
Message 10 of 39

komondormrex
Mentor
Mentor

sure thing with one exception. selected attributes cannot be highlighted or other way distinguished with autocad functionality. 

0 Likes
Message 11 of 39

thomas_schluesselberger
Advocate
Advocate
I'm not sure if i understand you right.
Do you mean that i can't select the attributes and then execute the LISP?
0 Likes
Message 12 of 39

komondormrex
Mentor
Mentor

nope. i mean that you can't control what attributes you are selecting, because they will not be highlighted while selecting. 

0 Likes
Message 13 of 39

Moshe-A
Mentor
Mentor

@thomas_schluesselberger  hi,

 

Check this ATTRO (Attributes Rotate) command.

 

Command: ATTRO
Select text or attribute:
1 text object(s) selected.
Select text or attribute:
2 text object(s) selected.
Select text or attribute:

 

The program starts with selecting attribute or text. when done selecting, press enter. each selection is highlight but highlighting an attribute is barely seen on screen, so be aware on that. 

 

to comply with your request to rotate attribute while relocate them (which is two functions) and  to visualize dragging, the program first duplicate the attribute (by converting them to ordinary text objects) than uses MOVE command and than (acet-ss-drag-rotate) which is from ExpressTools API functions but the problem with (acet-ss-drag-rotate) function is while dragging and you press esc (or some other error occur) although it does break the dragging, it returns nil (instead of stopping the command) and the ATTRO command continues to work making unwanted result. so you need to use Undo to reverse that. Another issue could be on attributes where the rotation causes them to be in unreadable angle, attro reverse the angle to make it readable but if the justification point is not middle center, it could be moved.

 

beside that the command works fine 😀

 

enjoy

Moshe

 

 

(vl-load-com)

(defun c:attro (/ break data_input attrib->text askangle Justify qang ; local functions
		  adoc ss ename1 elist1 attribs^ p0 p1 p2 p3 rot cod)
  
 (defun break (msg)
  (if (not (wcmatch (strcase msg t) "*break,*cancel*,*exit*"))
   (princ (strcat "\nError: " msg))
  )
   
  (if ss 
   (foreach ent (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss)))
    (entdel ent) 
   )
  ); if
   
  (vla-endUndoMark adoc)
  (vlax-release-object adoc)
    
  (setvar "cmdecho" 1)
  (setq *error* nil)
   
  (princ)
 ); break

  
 (defun data_input (/ n pick0 ename0 elist0)
  (setq n 0)
  (while (setq pick0 (nentsel "\nSelect text or attribute: "))
   (cond
    ((and
      (setq elist0 (entget (setq ename0 (car pick0))))
      (wcmatch (cdr (assoc '0 elist0)) "TEXT,ATTRIB")
     )
     (redraw ename0 3) 
     (setq attribs^ (cons ename0 attribs^))
     (prompt (strcat "\n" (itoa (setq n (1+ n))) " text object(s) selected."))
    ); case
    ( t
     (vlr-beep-reaction)
     (prompt "\nObject selected is not text nor attribute.")
    ); case
   ); cond
  ); while
   
  attribs^
 ); data_input


 ; temporary convert attrib to text 
 (defun attrib->text (e)
  (entmake
   (append
    (list '(0 . "TEXT"))
    (vl-remove-if
     '(lambda (dxf)
       (member (car dxf) '(-1 0 2 3 5 70 72 74 100 102 280 330 347 360 370 440)) ; eliminate these dxf codes from elist
      )
     e
    ); vl-remove-if
   ); append
  ); entmake
 ); attdef->text


 ; pause for angle while draging 
 (defun askangle (def / ask)
  (if (not (setq ask (acet-ss-drag-rotate ss p1 (strcat "\nRotation angle <" (angtos def 0) ">: ") t 0)))
   (setq ask def)
   (setq def ask)
  )
 ); askangle


 ; return justification dxf code 
 (defun Justify (e / itm)
  (if (and
	(setq itm (assoc '11 e))
	(not (equal (distance (cdr itm) '(0.0 0.0 0.0)) 0.0 1e-3))
      )
   11 10)
 ); Justify 


 ; make sure text is in readable angle 
 (defun qang (a)
  (if (and (> a (* pi 0.5)) (<= a (* pi 1.5)))
   (+ a pi)
   a) 
 ); qang

  
 ; here start attro
 (setq *error* break)
 (setvar "cmdecho" 0)
  
 (setq adoc (vla-get-ActiveDocument (vlax-get-acad-object)))
 (vla-startUndoMark adoc)
   
 (if (> (abs (getvar "userr5")) (* pi 2))
  (setvar "userr5" 0.0)
 )

 (if (data_input)
  (progn 
   (setq ss (ssadd)) 
   (foreach ename1 attribs^
    (setq elist1 (entget ename1))
    (attrib->text elist1)
    (ssadd (entlast) ss) 
   ); foreach

   (if (and
	 (> (sslength ss) 0)
         (setq p0 (getpoint "\nSpecify base point: "))
	 (not (command "._move" "_si" ss p0 "'cmdecho" 1 pause))
	 (setq p1 (getvar "lastpoint"))
	 (setvar "userr5" (setq rot (askangle (getvar "userr5"))))
       )
    (progn
     (foreach ent (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss)))
      (entdel ent) 
     )
     (setq ss nil)
     
     (foreach ename1 attribs^ 
      (setq elist1 (entget ename1))
      (setq p2 (cdr (assoc (setq cod (Justify elist1)) elist1)))
      (setq p3 (polar p1 (+ rot (angle p0 p2)) (distance p0 p2)))
      (setq elist1 (subst (cons cod p3) (assoc cod elist1) elist1))
      (setq elist1 (subst (cons '50 (qang (+ rot (cdr (assoc '50 elist1))))) (assoc '50 elist1) elist1))
      (entmod elist1)
      (entupd ename1)
     ); foreach
    ); progn
   ); if
		
  ); progn  
 ); if

 (vla-endUndoMark adoc)
 (vlax-release-object adoc)
  
 (setvar "cmdecho" 1)
 (setq *error* nil)
   
 (princ)
); c:attro

 

0 Likes
Message 14 of 39

Moshe-A
Mentor
Mentor

@thomas_schluesselberger ,

 

Found a nice solution to the (acet-ss-drag-rotate) problem (which on pressing esc does not break the command) so now if under dragging you press esc, another prompt is issued:

 

Accept the [Default or Abort] <270>: 

 

choose Abort if you really aborting or just enter to accept the default angle or just Default.

at break, the temporary text(s) will be deleted and no Undo is needed - this is nice 😀

 

The specified rotation angle will be added to the existing text angle. say you specified 90, and the text(s) is already 90, the result is 180 but because of the (qang) fix angle function, the result will be 0.

 

enjoy

Moshe

 

 

(vl-load-com) ; load activex support

(defun c:attro (/ break clear_text data_input attrib->text relocate askangle Justify qang ; local functions
		  adoc ss ename1 elist1 attribs^ p0 p1 p2 p3 rot cod)
 
 (defun break (msg)
  (if (not (wcmatch (strcase msg t) "*break,*cancel*,*exit*"))
   (princ (strcat "\nError: " msg))
  )

  (clear_text) 
   
  (vla-endUndoMark adoc)
  (vlax-release-object adoc)
  (setq *error* nil)
  (princ)
 ); break


 ; delete temporary text 
 (defun clear_text ()
  (if ss
   (progn 
    (foreach ename (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss)))
     (entdel ename) 
    )
    (setq ss nil)
   ); progn
  ); if
 ); clear_text

  
 (defun data_input (/ n pick0 ename0 elist0)
  (setq n 0)
  (while (setq pick0 (nentsel "\nSelect text or attribute: "))
   (cond
    ((and
      (setq elist0 (entget (setq ename0 (car pick0))))
      (wcmatch (cdr (assoc '0 elist0)) "TEXT,ATTRIB")
     )
     (redraw ename0 3) 
     (setq attribs^ (cons ename0 attribs^))
     (prompt (strcat "\n" (itoa (setq n (1+ n))) " text object(s) selected."))
    ); case
    ( t
     (vlr-beep-reaction)
     (prompt "\nObject selected is not text nor attribute.")
    ); case
   ); cond
  ); while
   
  attribs^
 ); data_input


 ; temporary convert attrib to text 
 (defun attrib->text (e)
  (entmake
   (append
    (list '(0 . "TEXT"))
    (vl-remove-if
     '(lambda (dxf)
       (member (car dxf) '(-1 0 2 3 5 70 72 74 100 102 280 330 347 360 370 440)) ; eliminate these dxf codes from elist
      )
     e
    ); vl-remove-if
   ); append
  ); entmake
 ); attdef->text


 (defun relocate (/ AcDbText)
  (foreach ename (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss)))
   (setq AcDbText (vlax-ename->vla-object ename))
   (vla-move AcDbText (vlax-3d-point p0) (vlax-3d-point p1))
   (vlax-release-object AcDbText) 
  ); foreach
 ); relocate


 ; return rotation angle 
 (defun askangle (def / ask quiz)
  (if (not (setq ask (acet-ss-drag-rotate ss p1 (strcat "\nRotation angle <" (angtos def 0) ">: ") t 0)))
   (progn
    (initget "Default Abort")
    (if (not (setq quiz (getkword (strcat "\nAccept the [Default or Abort] <" (angtos def 0) ">: "))))
     (setq ask def)
     (setq ask nil)
    ); if
   ); progn
  ); if

  ask
 ); askangle

  
 ; return justification dxf code 
 (defun Justify (e / itm)
  (if (and
	(setq itm (assoc '11 e))
	(not (equal (distance (cdr itm) '(0.0 0.0 0.0)) 0.0 1e-3))
      )
   11 10)
 ); Justify 


 ; make sure text is in readable angle 
 (defun qang (a)
  (if (and (> a (* pi 0.5)) (<= a (* pi 1.5)))
   (+ a pi)
   a) 
 ); qang


 ; here start attro
 (setq *error* break)
 (setq adoc (vla-get-ActiveDocument (vlax-get-acad-object)))
 (vla-startUndoMark adoc)
   
 (if (> (abs (getvar "userr5")) (* pi 2))
  (setvar "userr5" 0.0)
 )

 (if (data_input) 
  (cond
   ((progn  
     (setq ss (ssadd)) 
     (foreach ename1 attribs^
      (setq elist1 (entget ename1))
      (attrib->text elist1)
      (ssadd (entlast) ss) 
     ); foreach
     (= (sslength ss) 0)
    ); progn
   ); case
   ((not (setq p0 (getpoint "\nSpecify move base point: "))))
   ((not (setq p1 (acet-ss-drag-move ss p0 "\nSpecify move second point: " t 0)))
    (clear_text)    
   ); case
   ((not (relocate)))
   ((not (setq rot (askangle (getvar "userr5"))))
    (clear_text)
   ); case
   (t
    (setvar "userr5" rot)
    (clear_text)
    (foreach ename1 attribs^ 
     (setq elist1 (entget ename1))
     (setq p2 (cdr (assoc (setq cod (Justify elist1)) elist1)))
     (setq p3 (polar p1 (+ rot (angle p0 p2)) (distance p0 p2)))
     (setq elist1 (subst (cons cod p3) (assoc cod elist1) elist1))
     (setq elist1 (subst (cons '50 (qang (+ rot (cdr (assoc '50 elist1))))) (assoc '50 elist1) elist1))
     (entmod elist1)
     (entupd ename1)
    ); foreach
   ); case
  ); cond
 ); if

 (vla-endUndoMark adoc)
 (vlax-release-object adoc)
 (setq *error* nil)
   
 (princ)
); c:attro

 

0 Likes
Message 15 of 39

komondormrex
Mentor
Mentor

hey, marking selected attributes with bounding box, et function.

 

 

 

;****************************************************************************************************************************

;	komondormrex, mar 2023

;****************************************************************************************************************************

(defun c:rotate_attributes (/ selecting_stopped nentsel_data exit_value selected_enames_boundings command_stopped
			      			  attribute_corner_points ename_list rotation_point previous_rotation_angle
			      			  error_occurred rotation_angle_set
							  polar_tracking_angle_list angle_index active_polar_tracking
			   			   )
	(vla-startundomark (vla-get-activedocument (vlax-get-acad-object)))
	(while (not selecting_stopped)
		(setq nentsel_data (vl-catch-all-apply 'nentsel (list "\nSelect block attribute: ")))
		(cond
			(
				(vl-catch-all-error-p nentsel_data)
					(setq selecting_stopped t
						  exit_value 0
					)
			 		(princ "\nCommand cancelled")
			)
			(
				(and
					 (null nentsel_data)
					 (= 52 (getvar 'errno))
				)
					(setq selecting_stopped t
						  exit_value 1
					)
			)
			(
				(and
					 (null nentsel_data)
					 (= 7 (getvar 'errno))
				)
					(if selected_enames_boundings (grvecs selected_enames_boundings))
			)
			(
				t
					(if (or
							(= "ATTRIB" (cdr (assoc 0 (entget (car nentsel_data)))))
							(and
								 (or
									(= "TEXT" (cdr (assoc 0 (entget (car nentsel_data)))))
									(= "MTEXT" (cdr (assoc 0 (entget (car nentsel_data)))))
								 )
								 (not (equal 'ename (type (car (last nentsel_data)))))
							)
						)
							(if (not (member (car nentsel_data) ename_list))
								(progn
									(setq ename_list (append ename_list (list (car nentsel_data))))
						  			(setq attribute_corner_points (acet-geom-textbox (entget (car nentsel_data)) 0))
									(grvecs (setq selected_enames_boundings
												  (append selected_enames_boundings
														 (append (list 1)
																 (list (car attribute_corner_points) (cadr attribute_corner_points)
																       (cadr attribute_corner_points) (caddr attribute_corner_points)
																       (caddr attribute_corner_points) (cadddr attribute_corner_points)
																       (cadddr attribute_corner_points) (car attribute_corner_points)
																 )
														 )
												  )
										)
									)
								)
							)
					)
			)
		)
	)
	(if (and ename_list (= 1 exit_value))
			(progn
				(setq rotation_point (getpoint "\nPick rotation point: ")
				      previous_rotation_angle 0
				)
			  	(princ "\r<Space> to end rotation, Left Click, Right Click to set angle <0.00>")
			  	(while (not command_stopped)

				  	(setq error_occurred (if (vl-catch-all-error-p (setq grread_data (vl-catch-all-apply 'grread (list t 12 0)))) t nil))
					(cond
						(
							error_occurred														;	command cancellation
								(setq command_stopped t)
						 		(foreach ename ename_list
									(vla-rotate (vlax-ename->vla-object ename)
									  	    (vlax-3d-point (trans rotation_point 1 0))
									  	    (* -1 rotation_angle)
									)
								)
						 		(princ "\nCommand cancelled")
								(redraw)
						)
						(
							(= 3 (car grread_data))                                        		;	left click
								(setq command_stopped t)
						)
						(
						 	(= 5 (car grread_data))		                                		;	cursor moving
								(redraw)
								(setq polar_tracking_angle_list nil
									  angle_index -1
								)
								(repeat (fix (/ (* 2 pi) (getvar 'polarang)))
									(setq polar_tracking_angle_list (append polar_tracking_angle_list (list (* (setq angle_index (1+ angle_index)) (getvar 'polarang)))))
								)
								(setq polar_tracking_angle_list (append polar_tracking_angle_list (list (* 2 pi))))
								(if (vl-some '(lambda (polar_tracking) (< (- (setq active_polar_tracking polar_tracking) (/ pi 36))
																		  (angle rotation_point (cadr grread_data))
																		  (+ polar_tracking (/ pi 36))
																	   )

											  )
											  polar_tracking_angle_list
									)
										(grdraw rotation_point (polar rotation_point active_polar_tracking (distance rotation_point (cadr grread_data))) 3)
										(progn
											(setq active_polar_tracking nil)
											(grdraw rotation_point (cadr grread_data) 1)
										)
								)
								(if (null active_polar_tracking)
									(progn
										(foreach ename ename_list
											(vla-rotate (vlax-ename->vla-object ename)
											  	    	(vlax-3d-point (trans rotation_point 1 0))
											  	    	(- (setq rotation_angle (angle rotation_point (cadr grread_data))) previous_rotation_angle)
											)
										)
						 				(setq previous_rotation_angle (angle rotation_point (cadr grread_data)))
									)
									(progn
										(foreach ename ename_list
											(vla-rotate (vlax-ename->vla-object ename)
													  	(vlax-3d-point (trans rotation_point 1 0))
													  	(- active_polar_tracking previous_rotation_angle)
											)
										)
										(setq rotation_angle active_polar_tracking
											  previous_rotation_angle active_polar_tracking
										)
									)
								)
						 		(princ (strcat "\r<Space> to end rotation, Left Click, Right Click to set angle <"
									       (angtos rotation_angle 0 2)
									       ">"
								       )
							        )
						)
				  		(
							(= 25 (car grread_data))		                                	;	right click
								(redraw)
						 		(setq rotation_angle_set (getangle rotation_point "\nSet rotation angle for attribute(s): "))
						 		(foreach ename ename_list
									(vla-rotate (vlax-ename->vla-object ename)
									  	    (vlax-3d-point (trans rotation_point 1 0))
									  	    (- rotation_angle_set rotation_angle)
									)
								)
						 		(setq command_stopped t)
						)
						(
						 	(equal '(2 32) grread_data)				       						;	space
						 		(setq command_stopped t)
						)
						(
							t
						)
					)
			  	)
				(foreach ename ename_list
					(setq ename_corner_points (acet-geom-textbox (entget ename) 0))
					(if (or
							(> pi (cdr (assoc 50 (entget ename))) (/ pi 2))
							(> (/ (* 5 pi) 3) (cdr (assoc 50 (entget ename))) pi)
						)
							(vla-rotate (vlax-ename->vla-object ename)
										(vlax-3d-point
											(trans (polar (car ename_corner_points)
														  (angle (car ename_corner_points) (caddr ename_corner_points))
														  (* 0.5 (distance (car ename_corner_points) (caddr ename_corner_points)))
												   )
												   1 0
											)
										)
										pi
							)
					)
				)
			)
	)
	(redraw)
  	(vla-endundomark (vla-get-activedocument (vlax-get-acad-object)))
  	(princ)
)

;****************************************************************************************************************************
0 Likes
Message 16 of 39

thomas_schluesselberger
Advocate
Advocate

Hi, hope you had a nice weekend!

 

I've tested both LISPs. And before i get to my results i got at testing i wanna thank you both for your work.

The codes come pretty close to the result i was hoping for, but there are a few improvement requests.

 

To the lisp of @Moshe-A :

 

- When selecting the attributes and missing them, the lisp stops (or when I've already 1 selected goes to the next step).

Maybe you can change it,  that when i didn't hit an attribute the command doesnt close, but just keeps on asking me to select an attribute. 

 

- Currently it's not possible to select mtexts. Would be nice if also possible.

 

- After selecting the basepoint of the rotation, the LISP ask's me to move the attributes. This function is really nice to have, but the "problem" is, that i have to select the rotation basepoint 2 times, when i dont want to move the attributes. So it would be nice to be able to just skip the step of selecting the movement point with rightclick. I tested this and i noticed it's already kind-of possible, but when pressing right click after selecting the rotation basepoint, i don't get a live-preview of my attributes.

 

- Currently i'm not able to use polarsnap when selecting the rotation angle. I'm only able to select the wanted angle by tiping it or when i set it roughly the angle i want. I have the same issue with the code from @komondormrex .

 

- Also when selecting the angle of the rotation, i don't really understand what this value means?

thomas_schlu_0-1678700916675.png

 

- One thing i noticed, when rotating normal texts is, that they dont get placed as seen in the preview. (see video "normal-text-not-rotating" attached). (the magenta "123" text, is the normal text, the blue and green texts are attributes)

 

 

- Another strange issue is: when i have rotated the attributes 180° (or 270°) and then try to use torient to get the the text to 0° (or 90°), it always pops back to 180° (or 270°). I have the same issue with the code from @komondormrex . When i use torient and set it to "most readable" it works. (see video "torrient-problem" attached)

 

To the lisp of @komondormrex :

 

- I really like the way, you show the selected attributes.

 

- The LISP only works with attributes, but i would need it, to work also with texts/mtexts.

 

- Just as the LISP of moshe, i'm not able to use polarsnap when selecting the rotation angle. I'm only able to select the wanted angle by tiping it or when i set it roughly the angle i want. 

 

- Just as the LISP of moshe, when i have rotated the attributes 180° (or 270°) and then try to use torient to get the the text to 0° (or 90°), it always pops back to 180° (or 270°). When i use torient and set it to "most readable" it works. (see video "torrient-problem" attached).

 

EDIT: The problems with torient where because of the block i was using. I tried it with a different block and it worked just fine. Pretty weird.

 

I hope you two understand all the things I wanted to describe to you.

And again, thank you already!

0 Likes
Message 17 of 39

komondormrex
Mentor
Mentor

really? 

Re: Rotate Attributes just like rotate command))) 

0 Likes
Message 18 of 39

thomas_schluesselberger
Advocate
Advocate

@komondormrexDo you mean that i haven't said that? 

Because don't get me wrong and i really don't want to be rude, but I already wrote that in the first message:

 

"It would be nice if the lisp is also able to rotate attributes and normal texts at one time."

 

 

0 Likes
Message 19 of 39

komondormrex
Mentor
Mentor

you bet)

0 Likes
Message 20 of 39

komondormrex
Mentor
Mentor

what do you mean by 'not be able to use polar snap'?
1. polar tracking?

2. @Anonymous<15?

3. else?

0 Likes