Move text to nearest closed polyline geometric center

Move text to nearest closed polyline geometric center

rjames94
Enthusiast Enthusiast
735 Views
5 Replies
Message 1 of 6

Move text to nearest closed polyline geometric center

rjames94
Enthusiast
Enthusiast

Hi, I'm in need of a lisp routine to place text inside the nearest closed polyline.

 

acad_6McuKnJDsm.gif

 

I found a lisp routine but I'm getting the following error, ; error: ActiveX Server returned the error: unknown name: Centroid. Not sure if I'm on the right track or not, but I'm guessing i need to fix GetPolylineCentroid function or am not passing the correct parameters in. I'm new to autolisp so any help would be greatly appreciated!

 

(defun C:MoveTextToCenter ( / ss ent pt1 plss pl centroid )
  ;; Function to calculate centroid of closed polyline
  (defun GetPolylineCentroid (ename / obj area cx cy pt lst)
    (setq obj (vlax-ename->vla-object ename)
          area (vla-get-area obj)
          cx (vla-get-centroid obj)
          pt (vlax-variant->list cx)
    )
    pt
  )
  
  ;; Main function
  (setq ss (ssget "_:L" '((0 . "TEXT,MTEXT"))))
  (if ss
    (progn
      (setq ent (ssname ss 0))
      (setq pt1 (cdr (assoc 10 (entget ent))))
      
      ;; Select nearby closed polyline
      (setq plss (ssget "_W" 
                       (list (- (car pt1) 100) (- (cadr pt1) 100))  ; Lower-left corner
                       (list (+ (car pt1) 100) (+ (cadr pt1) 100))  ; Upper-right corner
                       '((0 . "LWPOLYLINE") (-4 . "&") (70 . 1))    ; Closed polylines only
                 ))
      
      ;; If polyline found, get its centroid
      (if plss
        (progn
          (setq pl (ssname plss 0))
          (setq centroid (GetPolylineCentroid pl))
          (command "_.move" ent "" pt1 centroid)
          (princ "\nText moved to polyline centroid.")
        )
        (princ "\nNo closed polyline found within 100 units.")
      )
    )
    (princ "\nNo text object selected.")
  )
  (princ)
)

 

0 Likes
Accepted solutions (1)
736 Views
5 Replies
Replies (5)
Message 2 of 6

Kent1Cooper
Consultant
Consultant

Polylines don't have a Centroid VLA property [as you have been informed by the error message].  REGIONS do -- can you have it make a Region from the Polyline for the purpose?  Or, are they always similar to those in the image, so that you could use [for example] the midpoint between opposite corners?  Or, Geometric-Center Object Snap will work if there are not other things around that it might see instead of the one you want:

(osnap (vlax-curve-getStartPoint ThePolylineEntityName) "_gce")

-- no VLA object conversion needed.

 

What if your 100-unit outreach window finds more than one Polyline?

 

[I don't see any use made of taking the area -- you could omit that.]

Kent Cooper, AIA
0 Likes
Message 3 of 6

Kent1Cooper
Consultant
Consultant

Further questions:

Those in your video appear from the grip arrangement to be Center-justified plain Text.  In that case, the grip you move to the middle of the Polyline is not the (assoc 10) value in your code, but the (assoc 11) value instead -- 10 is the one at the left end of the baseline, which I wouldn't think you want used for the Moving.  Would you just want the middle of the object [whether Text or Mtext, and regardless of justification] at the middle of the Polyline?  I wouldn't think you want the insertion point regardless of justification, would you?

 

Toward that end, the bounding box would be the obvious way to find the middle.  But if it's Mtext, that can be thrown off by the defining box's width beyond the content, and/or by things like leading spaces [but not trailing spaces, and in plain Text not by leading ones, either].  Is there any reason for such objects to be Mtext?  [The M is for Multi-line, after all -- not a consideration in the video, at least.]  The simplest cover-all-bases thing would be to just Explode any that are Mtext, and then the bounding-box approach will do well, ignoring justification.  But it could also be given zero width, have leading spaces stripped off if present, and/or have its justification changed to Middle Center and use its insertion point, or maybe some other approaches.

 

And your routine seems to expect selection of a single label -- it would allow selection of more than one, but would process only one.  But it is surely possible to let you select multiple labels at once, rather than one at a time, and process all of them, if desired.

Kent Cooper, AIA
0 Likes
Message 4 of 6

Moshe-A
Mentor
Mentor
Accepted solution

@rjames94  hi,

 

check this version

 

enjoy

moshe

 

 

(defun c:MoveTextToCenter   (/ _midpt _dbase _dsort  ; local functions
			       ss0 ss1 ss2 i ent points^ texts^ AcDbRegion AcDbText cp MinPoint MaxPoint m0 m1 pt short)
 ; anonymous functions
 (setq _midpt (lambda (t0 t1) (mapcar (function (lambda (x0 x1) (/ (+ x0 x1) 2))) t0 t1)))
 (setq _dbase (lambda (pt lst) (mapcar (function (lambda (itm) (cons (distance pt (cadr itm)) itm))) lst)))
 (setq _dsort (lambda (lst) (vl-sort lst (function (lambda (e0 e1) (< (car e0) (car e1)))))))
 

 (setvar "cmdecho" 0)
 (command "._undo" "_begin") 
  
 (if (setq ss0 (ssget '((0 . "text,mtext,lwpolyline"))))
  (progn
   (setq i -1 ss1 (ssadd) ss2 (ssadd))
   (repeat (sslength ss0)
    (setq ent (ssname ss0 (setq i (1+ i))))
    (if (wcmatch (strcase (cdr (assoc '0 (entget ent)))) "TEXT,MTEXT") 
     (ssadd ent ss2)
     (ssadd ent ss1)
    ); if
   ); repeat

   ; collect centroid points
   (foreach ent (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss1)))
    (command "._copy" "_si" ent "0,0,0" "0,0,0")
    (command "._region" "_si" ent)
    (setq AcDbRegion (vlax-ename->vla-object (entlast)))
    (setq cp (vlax-safearray->list (vlax-variant-value (vla-get-centroid AcDbRegion))))
    (setq points^ (cons cp points^))
    (vla-delete AcDbRegion)
    (vlax-release-object AcDbRegion)
   ); foreach

   ; collect middle text points
   (foreach ent (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss2)))
    (setq AcDbText (vlax-ename->vla-object ent))  
    (vla-GetBoundingBox AcDbText 'MinPoint 'MaxPoint)
    (setq m0 (vlax-safearray->list MinPoint))
    (setq m1 (vlax-safeArray->list MaxPoint))
    (setq texts^ (cons (cons ent (list (_midpt m0 m1))) texts^))
    (vlax-release-object AcDbText)
   ); foreach

   (foreach pt points^
    (if (setq short (car (_dsort (_dbase pt texts^))))
     (progn
      (command "._move" "_si" (cadr short) (caddr short) pt)
      (setq texts^ (vl-remove short texts^))
     ); progn
    ); if
   ); foreach
  ); progn
 ); if

 (command "._undo" "_end")
 (setvar "cmdecho" 1)
  
 (princ)
); c:MoveTextToCenter 

 

 

 

0 Likes
Message 5 of 6

rjames94
Enthusiast
Enthusiast

I'm getting ; error: Automation Error. Region is not on the UCS plane when trying to run

 

EDIT: Once I flatten the polylines everything works as expected. Thank you very much!

0 Likes
Message 6 of 6

Moshe-A
Mentor
Mentor

can you run it in world ucs?

0 Likes