Changing mleader aligner function to apply to all mleaders in model space

Changing mleader aligner function to apply to all mleaders in model space

Anonymous
Not applicable
2,356 Views
18 Replies
Message 1 of 19

Changing mleader aligner function to apply to all mleaders in model space

Anonymous
Not applicable

My code takes a given mleader, and snaps it to make the leader line at a 90 degree angle, based on what's closest. Could this be scaled into something that works for all modelspace mleaders? What I have been attempting has been to have it take the rotation of the mleader in modelspace to translate the points. I thought this would be quicker than cycling through each viewport, knowing that all mleaders will be at 90 degrees within their respective viewports. I was mainly just struggling with the maths for the rotational transformations, and I'm not very good with using mapcar/foreach 

 

(vl-load-com)
(defun C:MAL (/ ent vlent out vlout bp lp vlp newlp )
(while
	(setq ent (car (entsel)))
	(setq vlent (vlax-ename->vla-object ent))
	(setq vlout (vla-getleaderlinevertices vlent 0))
	(princ (vlax-safearray-get-dim (vlax-variant-value vlout)))
	(setq out (vlax-safearray->list (vlax-variant-value vlout)))
	(setq bp (list (nth 0 out) (nth 1 out) (nth 2 out)))
	(setq lp (list (nth 3 out) (nth 4 out) (nth 5 out)))
	(setq bp (trans bp 0 2))
	(setq lp (trans lp 0 2))
	(if (>= (abs (- (car bp) (car lp))) (abs (- (cadr bp) (cadr lp))))
		(progn
			(setq newlp (list (car lp) (cadr bp) 0.0))
			(setq newlp (trans newlp 2 0))
			(setq vlp (vlax-make-safearray 5 '(0 . 5)))
			(vlax-safearray-fill vlp (append (trans bp 2 0) newlp))
			(vla-setleaderlinevertices vlent 0 vlp)
		)
		(progn	
			(setq newlp (list (car bp) (cadr lp) 0.0))
			(setq newlp (trans newlp 2 0))
			(setq vlp (vlax-make-safearray 5 '(0 . 5)))
			(vlax-safearray-fill vlp (append (trans bp 2 0) newlp))
			(vla-setleaderlinevertices vlent 0 vlp)
		)
	)
)
(princ)
)
0 Likes
Accepted solutions (1)
2,357 Views
18 Replies
Replies (18)
Message 2 of 19

dlanorh
Advisor
Advisor

Why are you transforming the coords to display coords (trans bp 0 2)?

I am not one of the robots you're looking for

0 Likes
Message 3 of 19

Anonymous
Not applicable
So that way it can be used in a viewport. I'm not sure if that's the best
solution though
0 Likes
Message 4 of 19

dlanorh
Advisor
Advisor

I'm no expert on MLeaders, but I think they are 2D objects ie they are akin to LWPolylines in that they can have an elevation (z value) but not different z values for leader vertices. You would however have to account for any USC. So you need to trans to UCS.

 

Having test in an isometric view the display (2) fails but ucs (1) works.

 

I have adjusted you code accordingly and taken the opportunity to tidy it up a bit. To work for all MLeaders you need to use a selection set. The code below will process all MLeaders in the drawing, model and paperspace. If you want to only process Modelspace, see the comment alongside the ssget.

(defun rh:sammlung_n (o_lst grp / tmp n_lst)
  (setq n_lst nil)
  (cond ( (and o_lst (= (rem (length o_lst) grp) 0))
          (while o_lst
            (repeat grp (setq tmp (cons (car o_lst) tmp) o_lst (cdr o_lst)))
            (setq n_lst (cons (reverse tmp) n_lst) tmp nil)
          );end_while
        )
  );end_cond
  (if n_lst (reverse n_lst))
);end_defun

(vl-load-com)

(defun C:MAL (/ ss cnt vlent v_xyz bp lp newlp)
  (setq ss (ssget "_X" '((0 . "MULTILEADER"))));; add (410 . "Model") to filter list for model space objects only '((0 . "MULTILEADER") (410 . "Model"))
  (cond (ss
          (repeat (setq cnt (sslength ss))
            (setq vlent (vlax-ename->vla-object (ssname ss (setq cnt (1- cnt)))))
            (setq v_xyz (rh:sammlung_n (vlax-invoke vlent 'getleaderlinevertices 0) 3))
            (setq bp (trans (car v_xyz) 0 1))
            (setq lp (trans (cadr v_xyz) 0 1))
            (if (>= (abs (- (car bp) (car lp))) (abs (- (cadr bp) (cadr lp))))
              (setq newlp (trans (list (car lp) (cadr bp) 0.0) 1 0))
              (setq newlp (trans (list (car bp) (cadr lp) 0.0) 1 0))
            );end_if
            (vlax-invoke vlent 'setleaderlinevertices 0 (apply 'append (list bp newlp)))
          );end_repeat
          (princ (strcat (itoa (sslength ss)) "MultiLeaders processed"))
        )
        (t (princ "Nothing Found"))
  );end_cond
  (princ)
)

I have included my collection function (rh:sammlung_n). This takes the returned coordinates and groups them into points controlled by the grp variable.

 

You will notice that I have used the undocumented (vlax-invoke) method to return the leader line vertices. This side steps the variant returned by the documented methods (not conversions to do as it returns a list.

 

I have also used this to return the new leader vertices. The (apply 'append lst) converts a list of list into the required single list required, and also negates the need to mess about with variant arrays of point. It also mades the code slightly faster.

 

If you have any questions, let me know.

 

I am not one of the robots you're looking for

Message 5 of 19

Anonymous
Not applicable

Hey thank you for this, it works great, and much faster than my current code. I think it's missing a part though, sorry I didn't explain as well as I should have. What I've been working on is to add a conversion step in the calculation to account for a difference in rotation. As in, the code assumes all leaders are at 90 degrees in their respective UCS, and so it takes the difference in rotation from 90 degrees to convert the points so that the end result is as pictured.CurrentCurrentDesiredDesired

I know it's kind of a weird solution. To put it into context, we have viewports running along an alignment at varying angles, placed into modelspace. I'm hoping this code could fix them all in one hit.

0 Likes
Message 6 of 19

dlanorh
Advisor
Advisor
Will take a look tomorrow (midnight here), I'm assuming you mean the leader/dogleg and text angle.

I am not one of the robots you're looking for

0 Likes
Message 7 of 19

Anonymous
Not applicable

desired.PNG

Yeah so essentially, the result would be the equivalent of setting the UCS to align with the leader text, then running the MAL command. But for every leader. I assume it would use a matrix conversion formula. I think the formula would be:
x' = x cos *angle* - y sin *angle*
y' = x sin *angle* - y cos *angle*

Although I'm not sure, I kinda suck at trigonometry.

0 Likes
Message 8 of 19

dlanorh
Advisor
Advisor

Attached is a drawing with three possible outcomes, in two the initial leader is snapped to nearest 90 degrees (leader and text move) , in the third the text (and thus the dogleg) are aligned at 90 degrees to the initial leader line (text and dogleg rotate). Thoughts?

I am not one of the robots you're looking for

0 Likes
Message 9 of 19

Anonymous
Not applicable

I won't be at my computer until tomorrow (12.5 hours from this message) so I'll let you know then. Sorry about the delay

0 Likes
Message 10 of 19

dlanorh
Advisor
Advisor

No problem.

I am not one of the robots you're looking for

0 Likes
Message 11 of 19

Anonymous
Not applicable

result.PNG

Yeah that first example is what I'm going for. What I've got in the picture is the desired end result. The rectangles are indicative of paperspace viewport boundaries at various rotations. The code I'm trying to create would assume the text is at 90 degrees in paperspace, and use trigonometry to run the code on each mleader, as if it had run it on them when they were at 90 degrees in the UCS. Sorry it's kind of hard to explain. As an example of where we would use this, we could receive a dwg with 30 layouts, each layout having a viewport looking at a section of road. The views on each layout are all aligned with the road, so all different angles. Someone less meticulous than us may have used mleaders to label services, power poles, locations to be tested etc, and just plonked them in wherever looks nice (as they are in the START example of your dwg). The mleaders are all in modelspace. I could make a code that goes to each viewport and runs the command on all mleaders in the viewport, but I thought that would be a lot slower than it needed to be. So I would like it to do them all in one hit, from modelspace, using the assumption that the text is at 90 degrees in their respective viewports. The end result would be the image I have attached.

0 Likes
Message 12 of 19

dlanorh
Advisor
Advisor
The lisp is ready, but I won't be back in the office until tomorrow afternoon (site visit am). I will post it then.

I am not one of the robots you're looking for

0 Likes
Message 13 of 19

Anonymous
Not applicable

Thank you very much 🙂

0 Likes
Message 14 of 19

dlanorh
Advisor
Advisor
Accepted solution

As promised.

 

(defun rh:sammlung_n (o_lst grp / tmp n_lst)
  (setq n_lst nil)
  (cond ( (and o_lst (= (rem (length o_lst) grp) 0))
          (while o_lst
            (repeat grp (setq tmp (cons (car o_lst) tmp) o_lst (cdr o_lst)))
            (setq n_lst (cons (reverse tmp) n_lst) tmp nil)
          );end_while
        )
  );end_cond
  (if n_lst (reverse n_lst))
);end_defun

(vl-load-com)

(defun C:MAL (/ *error* c_doc c_spc ss cnt obj v_xyz b_pt e_pt r_pt)

  (defun *error* ( msg )
    (if (not (wcmatch (strcase msg) "*BREAK*,*CANCEL*,*EXIT*")) (princ (strcat "\nOops an Error : " msg " occurred.")))
    (princ)
  );end_*error*_defun

  (setq c_doc (vla-get-activedocument (vlax-get-acad-object))
        c_spc (vlax-get c_doc 'modelspace)                    ;;gets modelspace object
        ss (ssget "_X" '((0 . "MULTILEADER") (410 . "Model")));; filter list for model space objects only
  );end_setq

  (cond (ss
          (repeat (setq cnt (sslength ss))
            (setq obj (vlax-ename->vla-object (ssname ss (setq cnt (1- cnt))))
                  v_xyz (rh:sammlung_n (vlax-invoke obj 'getleaderlinevertices 0) 3)
                  b_pt (trans (car v_xyz) 0 1)  ;base point (arrowhead)
                  e_pt (trans (cadr v_xyz) 0 1) ;endpoint
            );end_setq
            (if (>= (abs (- (car b_pt) (car e_pt))) (abs (- (cadr b_pt) (cadr e_pt))))
              (setq x_obj (vla-addxline c_spc (vlax-3d-point e_pt) (vlax-3d-point (mapcar '+ e_pt '(0.0 -1.0 0.0)))));makes a vertical xline in modelspace at endpoint
              (setq x_obj (vla-addxline c_spc (vlax-3d-point e_pt) (vlax-3d-point (mapcar '+ e_pt '(1.0 0.0 0.0)))));makes a horizontal xline in modelspace at endpoint
            );end_setq
            (setq r_pt (trans (vlax-curve-getclosestpointto x_obj b_pt) 1 0));this finds the closest point on the xline to the base point. This will be perpendicular to the xline
            (vla-delete x_obj)  ;delete the xline
            (vlax-invoke obj 'setleaderlinevertices 0 (apply 'append (list b_pt r_pt)))
          );end_repeat
          (vla-regen c_doc acAllViewports)
          (princ (strcat (itoa (sslength ss)) "MultiLeaders processed"))
        )
        (t (princ "Nothing Found"))
  );end_cond
  (princ)
)

I've cheated and let AutoCAD do the work by inserting a vertical or horizontal xline through the end of the leader vertex, and then used the vlax-curve-getclosestpointto function to return the perpendicular point on the xline.

 

Any problems let me know.

I am not one of the robots you're looking for

Message 15 of 19

Anonymous
Not applicable
Uhh, I'm really sorry but I don't think this does what I was hoping for.
You've given me a lot to work with though, I'll see how I go working on it
further on my own. I'll accept as solution since you've given me a lot of
improvements to my code that have helped a lot. Thank you so much for all
your efforts.
0 Likes
Message 16 of 19

dlanorh
Advisor
Advisor
If you need any further help just shout.

I am not one of the robots you're looking for

0 Likes
Message 17 of 19

Anonymous
Not applicable

Just an update, think I've got it working (after a great deal of trial and error). Here's my go at it.

(defun rh:sammlung_n (o_lst grp / tmp n_lst)
  (setq n_lst nil)
  (cond ( (and o_lst (= (rem (length o_lst) grp) 0))
          (while o_lst
            (repeat grp (setq tmp (cons (car o_lst) tmp) o_lst (cdr o_lst)))
            (setq n_lst (cons (reverse tmp) n_lst) tmp nil)
          );end_while
        )
  );end_cond
  (if n_lst (reverse n_lst))
);end_defun

(vl-load-com)

(defun C:MALL (/ *error* c_doc c_spc ss cnt obj v_xyz b_pt e_pt r_pt)

	(defun *error* ( msg )
		(if (not (wcmatch (strcase msg) "*BREAK*,*CANCEL*,*EXIT*")) (princ (strcat "\nOops an Error : " msg " occurred.")))
		(princ)
	);end_*error*_defun
	
	(setq c_doc (vla-get-activedocument (vlax-get-acad-object))
		c_spc (vlax-get c_doc 'modelspace)                    ;;gets modelspace object
		ss (ssget "_X" '((0 . "MULTILEADER") (410 . "Model")));; filter list for model space objects only
	);end_setq
	
	(cond (ss
		(repeat (setq cnt (sslength ss))
			(setq obj (vlax-ename->vla-object (ssname ss (setq cnt (1- cnt))))
				v_xyz (rh:sammlung_n (vlax-invoke obj 'getleaderlinevertices 0) 3)
				b_pt (trans (car v_xyz) 0 1)  ;base point (arrowhead)
				e_pt (trans (cadr v_xyz) 0 1) ;endpoint
			);end_setq
			(if (>= (abs (- (car b_pt) (car e_pt))) (abs (- (cadr b_pt) (cadr e_pt))))
				(setq x_obj (vla-addxline c_spc (vlax-3d-point b_pt) (vlax-3d-point (mapcar '+ b_pt '(0.0 -1.0 0.0)))));makes a vertical xline in modelspace at endpoint
				(setq x_obj (vla-addxline c_spc (vlax-3d-point b_pt) (vlax-3d-point (mapcar '+ b_pt '(1.0 0.0 0.0)))));makes a horizontal xline in modelspace at endpoint
			);end_setq
			(command "rotate" (vlax-vla-object->ename x_obj) "" b_pt (rtos (* 57.296 (- 6.28318 (cdr (assoc 42 (entget (vlax-vla-object->ename obj))))))))
			(setq r_pt (trans (vlax-curve-getclosestpointto x_obj e_pt) 1 0));this finds the closest point on the xline to the base point. This will be perpendicular to the xline
			(vla-delete x_obj)  ;delete the xline
			(vlax-invoke obj 'setleaderlinevertices 0 (apply 'append (list b_pt r_pt)))
		);end_repeat
	(vla-regen c_doc acAllViewports)
	(princ (strcat (itoa (sslength ss)) "MultiLeaders processed"))
	)
	(t (princ "Nothing Found"))
	);end_cond
	(princ)
)
0 Likes
Message 18 of 19

Anonymous
Not applicable

Nevermind I messed up. This code evaluates x2-x1 >= y2-y1 based on the world UCS instead of the imagined UCS I'm creating using the xlines. So I'll still need to do the rotational transformations to figure which line it's drawing, for it to snap to.

0 Likes
Message 19 of 19

Anonymous
Not applicable
(defun getdempts (o_lst grp / tmp n_lst)
  (setq n_lst nil)
  (cond ( (and o_lst (= (rem (length o_lst) grp) 0))
          (while o_lst
            (repeat grp (setq tmp (cons (car o_lst) tmp) o_lst (cdr o_lst)))
            (setq n_lst (cons (reverse tmp) n_lst) tmp nil)
          );end_while
        )
  );end_cond
  (if n_lst (reverse n_lst))
);end_defun

(vl-load-com)

(defun C:MALL (/ *error* c_doc c_spc ss cnt obj v_xyz b_pt e_pt r_pt theta xtrigb_pt ytrigb_pt xtrige_pt ytrige_pt b_pttrigdelta e_pttrigdelta)

	(defun *error* ( msg )
		(if (not (wcmatch (strcase msg) "*BREAK*,*CANCEL*,*EXIT*")) (princ (strcat "\nOops an Error : " msg " occurred.")))
		(setvar "ctab" oldlayout)
		(princ)
	);end_*error*_defun
	
	(setq oldlayout (getvar "ctab"))
	(setvar "ctab" "Model")
	(setq c_doc (vla-get-activedocument (vlax-get-acad-object))
		c_spc (vlax-get c_doc 'modelspace)                    ;;gets modelspace object
		ss (ssget "_X" '((0 . "MULTILEADER") (410 . "Model")));; filter list for model space objects only
	);end_setq
	
	(cond (ss
		(repeat (setq cnt (sslength ss))
			(setq obj (vlax-ename->vla-object (ssname ss (setq cnt (1- cnt))))
				v_xyz (getdempts (vlax-invoke obj 'getleaderlinevertices 0) 3)
				b_pt (trans (car v_xyz) 0 1)  ;base point (arrowhead)
				e_pt (trans (cadr v_xyz) 0 1) ;endpoint
			);end_setq
			(setq theta (* 57.296 (- 6.28318 (cdr (assoc 42 (entget (vlax-vla-object->ename obj)))))))
			(setq xtrigb_pt 
				(- 
					(* (cos theta) (car b_pt)) 
			    (* (sin theta) (cadr b_pt))
			  )
			)
			(setq ytrigb_pt
				(- 
					(* (sin theta) (car b_pt)) 
			    (* (cos theta) (cadr b_pt))
			  )
			)
			(setq xtrige_pt
				(-
			 		(* (cos theta) (car e_pt))
			 		(* (sin theta) (cadr e_pt))
			  )
			)
			(setq ytrige_pt
				(-
			 		(* (sin theta) (car e_pt))
			 		(* (cos theta) (cadr e_pt))
			  )
			)
			(setq xtrigdelta (abs (- xtrigb_pt xtrige_pt)))
		  (setq ytrigdelta (abs (- ytrigb_pt ytrige_pt)))
			(if (>= xtrigdelta ytrigdelta)
					(setq x_obj (vla-addxline c_spc (vlax-3d-point b_pt) (vlax-3d-point (mapcar '+ b_pt '(0.0 -1.0 0.0)))));makes a vertical xline in modelspace at endpoint
					(setq x_obj (vla-addxline c_spc (vlax-3d-point b_pt) (vlax-3d-point (mapcar '+ b_pt '(1.0 0.0 0.0)))));makes a horizontal xline in modelspace at endpoint
			);end if
			(command "rotate" (vlax-vla-object->ename x_obj) "" b_pt (rtos (* 57.296 (- 6.28318 (cdr (assoc 42 (entget (vlax-vla-object->ename obj))))))))
			(setq r_pt (trans (vlax-curve-getclosestpointto x_obj e_pt) 1 0));this finds the closest point on the xline to the base point. This will be perpendicular to the xline
			(vla-delete x_obj)  ;delete the xline
			(vlax-invoke obj 'setleaderlinevertices 0 (apply 'append (list b_pt r_pt)))
		);end_repeat
	(vla-regen c_doc acAllViewports)
	(princ (strcat (itoa (sslength ss)) "MultiLeaders processed"))
	)
	(t (princ "Nothing Found"))
	);end_cond
	(setvar "ctab" oldlayout)
	(princ)
)

This doesn't quite work but you see where it's headed. Theoretically this way could be done without the xlines, but I'm on holiday for 3 weeks as of now so I'm pretty happy leaving it as it is for now. If anyone else is interested in a challenge you're welcome to have a go, otherwise I'll eventually get around to it and post here. Thanks again dlanorh

0 Likes