Visual LISP, AutoLISP and General Customization
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

MText 2 Multileader

22 REPLIES 22
SOLVED
Reply
Message 1 of 23
troma
11367 Views, 22 Replies

MText 2 Multileader

I got this nice little lisp from http://autocadtips.wordpress.com/2012/03/12/add-leader-to-text-make-multileader/

 

(defun c:mt2ml ( / oobj nobj nstrg)
  (vl-load-com)
  (setq oobj (vlax-ename->vla-object (car (nentsel "\nSelect source text: "))))
  (if (= (vlax-get-property oobj 'ObjectName) "AcDbMText")
    (setq nstrg (vlax-get-property oobj 'TextString))
    (exit)	   
    )
  (command "_MLEADER")
  (while (= 1 (logand (getvar "CMDACTIVE") 1)) (command PAUSE))
  (setq nobj (vlax-ename->vla-object (entlast)))
  (if (= (vlax-get-property nobj 'ObjectName) "AcDbMLeader")
    (vlax-put-property nobj 'TextString nstrg)
    (exit)	   
    )
  (entdel (vlax-vla-object->ename oobj))
  (princ)
)

 

 

It converts MText to a Multileader.

 

For my purposes, it would be great to add a couple of features to this.  Can anyone help me please?  I understand Latin and Greek better than Lisp.  Thanks.

 

My goals would be as follows:

 

  1. The multileader should end up on the same layer as the Mtext.  (Currently it uses the current layer.)
  2. The multileader should hold the same rotation as the Mtext. (Currently it goes to zero.)
  3. The multileader should be created in the same location as the Mtext, without the user having to click.  (Currently the multileader is created wherever the user clicks.)
  4. The multileader should be created without a leader.  We can add one afterwards if needed, but for many we don't need a leader.  (We're just using the object as mtext with a box around it.)

Thanks for any pointers or help.  Please let me know if some of my goals are unreasonable.


Mark Green

Working on Civil 3D in Canada

22 REPLIES 22
Message 2 of 23
mid-awe
in reply to: troma

Looking around I found:

 

http://www.theswamp.org/index.php?topic=30934.msg364891#msg364891

 

That version seems to have a good start on your goals.

Message 3 of 23
troma
in reply to: mid-awe

Any chance you can copy that over here for me please? I hear their registration program is somewhat onerous. (You need to be logged in to see that page.)
Thanks.

Mark Green

Working on Civil 3D in Canada

Message 4 of 23
mid-awe
in reply to: troma

From what I can see, this is the latest version; but, you will need to see the page to read the changes and improvements over the version you posted.

;;; AUTHOR
;;; Copyright© 2009 Ron Perez (ronperez@gmail.com)
;;; Inserts selected text\mtext into an mleader. If an associated leader is found
;;; with the selected text it is used for the new mleader location. Else an option to select a
;;; leader is given. If still nothing, two points are required and an mleader is
;;; created.   Enjoy :)

(defun c:txt2ml	(/	      allleaders   ent		elst	     ldrpts
		 ml	      objlst	   pt1		pt2	     ss
		 txt	      w		   x		rjp-getbbwdth
		 rjp-getpoints		   rjp-getassociatedleader
		)
  (vl-load-com)
  (defun rjp-getbbwdth (obj / out ll ur)
    (vla-getboundingbox obj 'll 'ur)
    (setq out (mapcar 'vlax-safearray->list (list ll ur)))
    (distance (car out) (list (caadr out) (cadar out)))
  )
  (defun rjp-getpoints (ent)
    (mapcar 'cdr (vl-remove-if-not '(lambda (x) (= 10 (car x))) (entget ent)))
  )
  (defun rjp-getassociatedleader (ent / pts)
    (if	(and (setq ent (cdadr (member '(102 . "{ACAD_REACTORS") (entget ent))))
	     (setq pts (rjp-getpoints ent))
	)
      (list (car pts) (cadr pts) ent)
    )
  )
  (if (setq ss (ssget '((0 . "*TEXT"))))
    (progn
      (setq txt	   (apply 'strcat
			  (mapcar
			    'cdr
			    (vl-sort
			      (mapcar '(lambda (x)
					 (cons (vlax-get x 'insertionpoint)
					       (strcat (vlax-get x 'textstring) " ")
					 )
				       )
				      (setq objlst (mapcar 'vlax-ename->vla-object
							   (setq elst (vl-remove-if
									'listp
									(mapcar 'cadr (ssnamex ss))
								      )
							   )
						   )
				      )
			      )
			      (function (lambda (y1 y2) (< (cadr (car y2)) (cadr (car y1)))))
			    )
			  )
		   )
	    w	   (car (vl-sort (mapcar 'rjp-getbbwdth objlst) '>))
	    txt	   (substr txt 1 (1- (strlen txt)))
	    ldrpts (car	(setq allleaders
			       (reverse
				 (vl-remove 'nil
					    (mapcar 'rjp-getassociatedleader elst)
				 )
			       )
			)
		   )
      )
      (mapcar 'vla-delete objlst)
      (cond ;;leader found for one of the selected text
	    ((and ldrpts
		  (setq pt1 (car ldrpts))
		  (setq pt2 (cadr ldrpts))
		  (setq ent (caddr ldrpts))
	     )
	    )
	    ;;Select a leader
	    ((and (princ "\nSelect leader to replace [Enter to pick new points]: ")
		  (setq ss (ssget '((0 . "leader"))))
		  (setq ent (ssname ss 0))
		  (setq ldrpts (rjp-getpoints ent))
		  (setq pt1 (car ldrpts))
		  (setq pt2 (cadr ldrpts))
	     )
	    )
	    ;;Just add a new leader
	    ((and (setq pt1 (getpoint "\nSpecify leader arrowhead location: "))
		  (setq pt2 (getpoint pt1 "\nSpecify landing location: "))
	     )
	    )
      )
      (if (and pt1 pt2)
	(progn (command "._MLEADER" pt1 pt2 "")
	       (setq ml (vlax-ename->vla-object (entlast)))
	       (vla-put-textstring ml txt)
	       (vla-put-textwidth ml w)
	       (if ent
		 (progn	(if (setq txt (cdr (assoc 340 (entget ent))))
			  (entdel txt)
			)
			(entdel ent)
		 )
	       )
	       (mapcar 'entdel (mapcar 'caddr (cdr allleaders)))
	)
      )
    )
    (princ)
  )
)

 

Message 5 of 23
troma
in reply to: mid-awe

Well, I'm not sure what that does, but it does something alright! The selected object (text or mtext) disappears. I don't know if it's deleted or just moved somewhere, but I couldn't find it with zoom extents or thaw all layers.

Mark Green

Working on Civil 3D in Canada

Message 6 of 23
mid-awe
in reply to: troma

Did you watch the prompts? After you select the text or Mtext, you are prompted to continue selecting objects. So, select your text or Mtext and press enter twice. You will be prompted with "Select leader to replace [Enter to pick new points]:", if you press enter you will be expected to click two points to place the Mleader with your text or Mtext. You could then select the Mleader object, right click, & select remove leader.

 

Also, I see some simple way to make a few changes for your needs. I'll try to peel away some time to make those changes unless someone else beats me to it.

Message 7 of 23
troma
in reply to: mid-awe

Well, to be honest, last time I was looking over my colleague's shoulder while I got him to test it out.  So I went and tried it for myself.

I watched the prompts.  You are asked to select a leader.  I tried selecting one, or I tried skipping past it and clicking points for a leader.  Either way, it glitches.  It asks me to enter text.  Whether I try to type something or not, it won't proceed correctly.  It does not automatically enter the text I selected; rather it asks me to enter the text, but at the same time does not actually allow me to do so.  I hit enter a few times to get it to proceed, and it will create a multileader object, with just a leader and no text.  I think if you hit enter and don't pick a leader or pick points for a leader, you don't even get the multileader object at all, which is what was happening to my coworker.

 

TXT2ML.PNG

 

Win 7 Pro, 32 bit; Intel Core i5 @ 2.80GHz; 4GB RAM

Civil 3D 2013, SP2, 32 bit


Mark Green

Working on Civil 3D in Canada

Message 8 of 23
mid-awe
in reply to: troma

Do you mind if I ask, "What version of AutoCAD are you using"?
Message 9 of 23
mid-awe
in reply to: mid-awe

I don't know your AutoCAD version, but anyway, here is my attempt to help with your struggle:

 

;;; AUTHOR
;;; Copyright© 2009 Ron Perez (ronperez@gmail.com)
;;; Inserts selected text\mtext into an mleader. If an associated leader is found
;;; with the selected text it is used for the new mleader location. Else an option to select a
;;; leader is given. If still nothing, two points are required and an mleader is
;;; created.   Enjoy :)

;;; Modified 2013 - By Chuck Middaugh (mid-awe at http://www.mid-awe.com)
;;; Modifications made to allow for:
;;;		- The multileader should end up on the same layer as the Mtext.
;;;		- The multileader should hold the same rotation as the Mtext.
;;;		- The multileader should be created in the same location as the Mtext, without the user having to click.
;;;		- The multileader should be created without a leader.  (The leader "Dog Leg" is still created even without clicks - Someone smarter than me can figure this out. Sorry.)

(DEFUN c:txt2ml	(/ allleaders ang clay ent elst	lay ldrpts ml objlst pt1 pt2 ss	txt w x	rjp-getbbwdth rjp-getpoints rjp-getassociatedleader)
  (VL-LOAD-COM)
  (SETQ clay (GETVAR "clayer"))
  (DEFUN acos (x)
    (COND ((EQUAL x -1 1e-6) PI)
	  ((EQUAL x 1 1e-6) 0.0)
	  ((< -1 x 1) (ATAN (SQRT (- 1 (* x x))) x))
    )
  )
  (DEFUN rjp-getbbwdth (obj / out ll ur)
    (VLA-GETBOUNDINGBOX obj 'll 'ur)
    (SETQ out (MAPCAR 'VLAX-SAFEARRAY->LIST (LIST ll ur)))
    (DISTANCE (CAR out) (LIST (CAADR out) (CADAR out)))
  )
  (DEFUN rjp-getpoints (ent)
    (MAPCAR 'CDR (VL-REMOVE-IF-NOT '(LAMBDA (x) (= 10 (CAR x))) (ENTGET ent)))
  )
  (DEFUN rjp-getassociatedleader (ent / pts)
    (IF	(AND (SETQ ent (CDADR (MEMBER '(102 . "{ACAD_REACTORS") (ENTGET ent))))
	     (SETQ pts (rjp-getpoints ent))
	)
      (LIST (CAR pts) (CADR pts) ent)
    )
  )
  (IF (SETQ ss (SSGET ":S" '((0 . "*TEXT"))))
    (PROGN
      (SETQ lay	(CDR (ASSOC 8 (ENTGET (SSNAME ss 0))))
	    pt2	(CDR (ASSOC 10 (ENTGET (SSNAME ss 0))))
	    ang	(VLA-GET-ROTATION (VLAX-ENAME->VLA-OBJECT (SSNAME ss 0)))
      )
      (PRIN1 ang)
      (SETVAR "clayer" lay)
      (SETQ txt	   (APPLY 'STRCAT
			  (MAPCAR 'CDR
				  (VL-SORT (MAPCAR '(LAMBDA (x) (CONS (VLAX-GET x 'insertionpoint) (STRCAT (VLAX-GET x 'textstring) " ")))
						   (SETQ objlst (MAPCAR 'VLAX-ENAME->VLA-OBJECT (SETQ elst (VL-REMOVE-IF 'LISTP (MAPCAR 'CADR (SSNAMEX ss))))))
					   )
					   (FUNCTION (LAMBDA (y1 y2) (< (CADR (CAR y2)) (CADR (CAR y1)))))
				  )
			  )
		   )
	    w	   (CAR (VL-SORT (MAPCAR 'rjp-getbbwdth objlst) '>))
	    txt	   (SUBSTR txt 1 (1- (STRLEN txt)))
	    ldrpts (CAR (SETQ allleaders (REVERSE (VL-REMOVE 'nil (MAPCAR 'rjp-getassociatedleader elst)))))
      )
      (MAPCAR 'VLA-DELETE objlst)
      (COND ;;leader found for one of the selected text
	    ((AND ldrpts
		  (SETQ pt1 (CAR ldrpts))
		  (SETQ pt2 (CADR ldrpts))
		  (SETQ ent (CADDR ldrpts))
	     )
	    )
	    ;;Select a leader
	    ((AND (PRINC "\nSelect leader to replace [Enter to pick new points]: ")
		  (SETQ ss (SSGET '((0 . "leader"))))
		  (SETQ ent (SSNAME ss 0))
		  (SETQ ldrpts (rjp-getpoints ent))
		  (SETQ pt1 (CAR ldrpts))
		  (SETQ pt2 (CADR ldrpts))
	     )
	    )
	    ;;Just add a new leader
	    ((IF (SETQ pt1 (GETPOINT "\nSpecify leader arrowhead location [Enter to skip]: "))
	       (SETQ pt2 (GETPOINT pt1 "\nSpecify landing location: "))
	     )
	    )
      )
      (IF (AND pt1 pt2)
	(PROGN (COMMAND "._MLEADER" pt1 pt2 "")
	       (SETQ ml (VLAX-ENAME->VLA-OBJECT (ENTLAST)))
	       (VLA-PUT-TEXTSTRING ml txt)
	       (VLA-PUT-TEXTWIDTH ml w)
	       (VLA-PUT-TEXTROTATION ml ang)
	       (IF ent
		 (PROGN	(IF (SETQ txt (CDR (ASSOC 340 (ENTGET ent))))
			  (ENTDEL txt)
			)
			(ENTDEL ent)
		 )
	       )
	       (MAPCAR 'ENTDEL (MAPCAR 'CADDR (CDR allleaders)))
	)
	(IF pt2
	  (PROGN (COMMAND "._MLEADER" pt2 pt2 "")
		 (SETQ ml (VLAX-ENAME->VLA-OBJECT (ENTLAST)))
		 (VLA-PUT-TEXTSTRING ml txt)
		 (VLA-PUT-TEXTWIDTH ml w)
		 (VLA-PUT-TEXTROTATION ml ang)
		 (IF ent
		   (PROGN (IF (SETQ txt (CDR (ASSOC 340 (ENTGET ent))))
			    (ENTDEL txt)
			  )
			  (ENTDEL ent)
		   )
		 )
		 (MAPCAR 'ENTDEL (MAPCAR 'CADDR (CDR allleaders)))
	  )
	)
      )
    )
    (PRINC)
  )
  (SETVAR "clayer" lay)
  (PRINC)
)

 I've tested this a few times on my setup and it is working. Hopefully this with help you.

Message 10 of 23
troma
in reply to: mid-awe

Thanks for your efforts, seriously.  I did put my spec's at the end of my last message, but here it is again: Civil 3D 2013, SP2, 32 bit.

 

Unfortunately, it is still not working.  It did work one time (and changed the text height, which was weird) but now it won't anymore, even on the same piece of text that it did work on.  It is hitting the same error:

Enter text: ; error: Automation Error. Description was not provided.

 

It is creating a multileader, with no text (just the dog-leg as you call it).  The edits you've made are working: it is on the right layer and orientation.  But there is no text.

 

Here is a look at when the error happens.  It is asking me to enter text, but it doesn't permit me to do so.  So I have to Cancel out and leave the command.

Incidentally, it leaves the current layer on the layer of the object I was editing.  Perhaps this is due to canceling the command without completing it.

TXT2ML2.PNG

 


Mark Green

Working on Civil 3D in Canada

Message 11 of 23
Lee_Mac
in reply to: troma

Here is a very rough draft of a program to generate MLeaders from a selection of MText.

 

The program will use the current MLeader Style at the time of running the program, and will adhere to the MText height, rotation & all other standard properties (such as Layer / Color / Linetype / Lineweight etc.), and should also be compatible with MText with any justification.

 

;; MText to MLeader (with no Leader)
;; Lee Mac  -  2013-11-19

(defun c:mt2ml ( / e h i j m r s w )
    (if (setq s (ssget "_:L" '((0 . "MTEXT"))))
        (repeat (setq i (sslength s))
            (setq e (entget (ssname s (setq i (1- i))))
                  r (cdr (assoc 50 e))
                  w (cdr (assoc 42 e))
                  h (cdr (assoc 43 e))
                  j (cdr (assoc 71 e))
            )
            (if
                (and
                    (setq m
                        (entmakex
                            (append
                               '(
                                    (000 . "MULTILEADER")
                                    (100 . "AcDbEntity")
                                    (100 . "AcDbMLeader")
                                )
                                (mapcar
                                   '(lambda ( x ) (cond ((assoc (car x) e)) ( x )))
                                   '(
                                        (008 . "0")
                                        (006 . "BYLAYER")
                                        (039 . 0.0)
                                        (062 . 256)
                                        (048 . 1.0)
                                        (370 . -1)
                                    )
                                )
                                (list
                                   '(300 . "CONTEXT_DATA{")
                                    (cons  041 (cdr (assoc 40 e)))
                                    (assoc 010 e)
                                   '(290 . 1)
                                    (cons  304 (cdr (assoc 1 e)))
                                    (list  111 (cos r) (sin r) 0.0)
                                    (list  112
                                        (cos (+ r (/ pi 2.0)))
                                        (sin (+ r (/ pi 2.0)))
                                        0.0
                                    )
                                   '(301 . "}")
                                    (cons 090 (+ 1024 65536 262144))
                                )
                            )
                        )
                    )
                    (setq m (entget m))
                    (entmod
                        (subst
                            (cons 12
                                (mapcar '+
                                    (mxv
                                        (list
                                            (list (cos r) (sin (- r)) 0.0)
                                            (list (sin r) (cos r)     0.0)
                                           '(0.0 0.0 1.0)
                                        )
                                        (list
                                            (cond
                                                ((member j '(2 5 8)) (/ w -2.0))
                                                ((member j '(3 6 9)) (- w))
                                                (0.0)
                                            )
                                            (cond
                                                ((member j '(4 5 6)) (/ h 2.0))
                                                ((member j '(7 8 9)) h)
                                                (0.0)
                                            )
                                            0.0
                                        )
                                    )
                                    (cdr (assoc 10 e))
                                )
                            )
                            (assoc 12 m)
                            m
                        )
                    )
                )
                (entdel (cdr (assoc -1 e)))
            )
        )
    )
    (princ)
)

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

(princ)

 

Note that the above is only compatible with MText residing in the WCS plane.

 

I hope this helps.

 

Lee

Message 12 of 23
troma
in reply to: troma

I meant to say: the green tick in the picture is the 'dog-leg' or 'landing' that has been created. The text that was there has been deleted by the command.

Mark Green

Working on Civil 3D in Canada

Message 13 of 23
mid-awe
in reply to: troma

It must be a vlisp issue. I am only able to test on AutoCAD 2013. Maybe someone else has an idea? I will continue looking it over to find what I can do.
Message 14 of 23
troma
in reply to: Lee_Mac

Lee, that's great!
Your comment crossed mine, so I didn't even notice it until I came back here an hour later. Very smooth.

Mark Green

Working on Civil 3D in Canada

Message 15 of 23
Lee_Mac
in reply to: troma


@troma wrote:
Lee, that's great!
Your comment crossed mine, so I didn't even notice it until I came back here an hour later. Very smooth.

Excellent, thank you troma - it was an interesting one to write Smiley Happy

Message 16 of 23
marlance
in reply to: Lee_Mac

hi Lee!

 

What if I want to add the mtext to an existing multileader?

Is that possible?

Message 17 of 23
gccdaemon
in reply to: marlance

You could probably pull the mtext string and append the selected mleader. Maybe this will get you started (don't know how to get/append the multileader string)? You could add on a section to create the mleader if none are existing. This one should repeat.

 

(defun C:MLSWAP (/ YN)
	(while	(or	(initget "Yes No")
			(not (setq YN (getkword "\nUse existing Multileader? [Yes/No] <Yes>: ")))
			(= YN "Yes")
		);or
		(SWAPTEXT)
	);while
	(princ "\nDone")
)

(defun SWAPTEXT ( / e1 e2 )
	(while (and	(setq e1 (SEL-TEXT "\nSelect text object <exit>: "))
			(setq e2 (SEL-TEXT "\nSelect Multileader <exit>: ")))
		(setq	e1 (entget e1)
			e2 (entget e2))
		(mapcar	'(lambda ( a b ) (entmod (subst (assoc 1 a) (assoc 1 b) b)))
			(list e1 e2)
			(list e2 e1)
		);and
	);while
	(princ)
)

(defun SEL-TEXT ( msg / ent )
    (while    (progn    (setvar 'errno 0)
            (setq ent (car (nentsel msg)))
            (cond    ((= 7 (getvar 'errno))
                (princ "\nNo text object selected:"))
            ((null ent) nil)
            (    (not (wcmatch (cdr (assoc 0 (entget ent))) "ATTRIB,*TEXT"))
                (princ "\nSelect a Text, MText or Attribute object:")
    )    )    )    )
    ent
)
(princ)

  I would love to see a finished product. Perhaps even let it reverse swap to put multileader string into text?

Andrew Ingram
Civil 3D x64 2019
Win 10 x64 Pro
Intel Xeon E5-1620
32 GB Ram
Message 18 of 23
troma
in reply to: Lee_Mac


@Lee_Mac wrote:

Here is a very rough draft of a program to generate MLeaders from a selection of MText.

 

The program will use the current MLeader Style at the time of running the program, and will adhere to the MText height, rotation & all other standard properties (such as Layer / Color / Linetype / Lineweight etc.), and should also be compatible with MText with any justification.

 

... 

Lee


Hi @Lee_Mac I just wanted to share a funny story.

After running this lisp pretty often for the last year or so, today is the first time I hit a snag. The converted object disappeared! I couldn't find it with zoom extents, and the previous selection set yielded no objects. Where could it be going? Frozen layer, UCS...?

Nope. Just me. I had forgotten to check my current MLeader Style. The current one was using a block as the leader object, not mtext. So I guess the mtext converted to a mleader with no leader and no mtext. That's the weirdest way of erasing something I can think of!  Smiley Very Happy


Mark Green

Working on Civil 3D in Canada

Message 19 of 23
CovenStine
in reply to: mid-awe

Hello,

I tried your LSP today, and it worked great!

I don't really use, and therefore don't like annotative labelling, so I went in, and made a layer style, applied it, and went along my merry way.

Five minutes later, and txt2ml yeilds the same error as the other user was complaining about

Specify next point:
Specify leader landing location:
Enter text: ; error: Automation Error. Description was not provided.

I couldn't understand it! It was working fine! ...before I applied my style.

I went in and poured over the style looking for a weird point that may be throwing the LSP for a loop.

Somehow, the "Maximum Leader Points" under "leader structure" had been turned up from 2 to 3.

By setting this back down to 2, everything worked great.

I should note, that this was for a STRAIGHT leader style, not SPLINE.

This doesn't affect me, because I don't NEED 3 point leaders, I just thought you might like to know.

Thanks for authoring this!

~C

Message 20 of 23
Anonymous
in reply to: Lee_Mac

Lee,

 

Do you have a version of this lisp that adds a leader to the converted mleader as well?

 

Thanks!

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Design & Make Report

”Boost