Lisp to select the points closest to the polyline

Lisp to select the points closest to the polyline

Oliver_C3D
Advocate Advocate
4,921 Views
13 Replies
Message 1 of 14

Lisp to select the points closest to the polyline

Oliver_C3D
Advocate
Advocate

Hello dear

For a long polyline and a large number of points around it, I need a lisp to identify, select, or output the points closest to the polyline.

Such a lisp really saves time as well as improves Selection accuracy.

0 Likes
Accepted solutions (2)
4,922 Views
13 Replies
Replies (13)
Message 2 of 14

Kent1Cooper
Consultant
Consultant

Not enough information....  The one point closest to it?  The seventeen points closest to it?  The hundred points closest to it?  The ask-the-User-how-many points closest to it?  All those within an ask-the-User-how-close distance?  Or would they all be at only a limited number of different distances, and you want all those at the one closest distance?  The two closest distances?  From among all Point objects in the drawing, or would the User select those among which to find the closest?  Etc., etc.

Kent Cooper, AIA
0 Likes
Message 3 of 14

Oliver_C3D
Advocate
Advocate

I want to explain to you explicitly like the attached file.
I have a playline with lots of points around it. The points closest to the polyline along the polyline are important to me and I want to select them.

0 Likes
Message 4 of 14

hak_vz
Advisor
Advisor

@Oliver_C3D 

In your sample dwg you actually don't have points. You have a huge set of zero length lines (sometime even duplicated).

In my almost 20 years practice working with geodetic maps, this is the worst way to create a point. Autocad has native object POINT. Its size and look is defined using command POINTSTYLE.  Eventually it can be coupled into a block if additional attributes are needed.

 

Here is a code that would select nearest POINT, create a circle around her, and store entity name to global variable np, that can be accessed using !np in command. It won't be of use for you but you can test it on well created sample.

 

 

(defun c:cppl ( / e eo n i pe pt di lst )
(setq e (car (entsel "\nSelect polyline >")))
(setq eo (vlax-ename->vla-object e))
(princ "\nSelect points >")
(setq n (sslength (setq sspoints (ssget '((0 . "POINT"))))) i 0)
(repeat n 
(setq pe (ssname sspoints i)) 
(setq pt (cdr (assoc 10 (entget pe))))
(setq di (distance (vlax-curve-getclosestpointto eo pt) pt))
(setq lst (cons (list di pe) lst))
(setq i (1+ i))
)
(setq np (cadar (vl-sort lst '(lambda (a b)(< (car a )(car b))))))
(command "circle" (cdr (assoc 10 (entget np))) 10)
(command "zoom" "O" np "")
(command "zoom" "S" 0.85)
(princ)
)

 

 

 

 

Miljenko Hatlak

EESignature

Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
0 Likes
Message 5 of 14

Kent1Cooper
Consultant
Consultant

The drawing does not answer the questions.  What constitutes "closest"?  There would need to be some kind of narrowing down of the possibilities.  I assume you would include this one:

closest1.PNG

But it's farther away than the ones at left and right -- by enough to disqualify it?  And this one, which is only barely farther from the path, but "feels" less likely to be wanted?

closest2.PNG

Is there a long-enough gap between the lower ones [that are obviously closer] that this one should be included?

 

Also, unfortunately, those are not Points, but tiny crosses made up of two Lines  [not quite zero-length -- 0.00002 units long], and in many cases four  Lines in two overlaid  crosses.  That would give you lots of duplicate identifications, whatever the criteria are.  A routine could theoretically consider all 2056 Lines in the drawing, and evaluate each one's distance from the path, and give you [one possible criterion] all of them that are less than 3 drawing units from it, in a list or a selection set.  But you'll get two or four items in such a list or selection set for every "point" location.  Would that be acceptable?  [You could reduce it to "only" two per location if you use OVERKILL on the drawing first.]

Kent Cooper, AIA
0 Likes
Message 6 of 14

Oliver_C3D
Advocate
Advocate

Yes, it is not right to measure small crosses. Instead, consider height texts.
My suggestion is to define a number, for example, a maximum of 3 meters. To select texts with a maximum distance of 3 meters from the polyline for us.

0 Likes
Message 7 of 14

Oliver_C3D
Advocate
Advocate

Ignore the small lines near the texts.
Because the distance of the texts from the playline is variable. My suggestion is to define a number, for example, a maximum of 3 meters. To select texts with a maximum distance of 3 meters from the polyline for us.

0 Likes
Message 8 of 14

Kent1Cooper
Consultant
Consultant
Accepted solution

@Oliver_C3D wrote:

.... consider height texts.
.... define a number, for example, a maximum of 3 meters. To select texts with a maximum distance of 3 meters from the polyline for us.


This seems to do that [once I realized the Mtexts are at their elevations, not at the level of the path Polyline], in minimal testing:

 

 

(defun C:Within3 (/ mss path n mt ins)
  (setq
    mss (ssget "_X" '((0 . "MTEXT")))
    path (ssname (ssget "_X" '((0 . "LWPOLYLINE"))) 0)
    within3 (ssadd); initially empty
  ); setq
  (repeat (setq n (sslength Mss))
    (setq mt (ssname mss (setq n (1- n))))
    (if
      (<
        (sqrt ; distance in XY only
          (-
            (expt
              (distance ; in 3D
                (setq ins (cdr (assoc 10 (entget mt))))
                (vlax-curve-getClosestPointTo path ins)
              ); distance
              2
            ); expt
            (expt (distof (cdr (assoc 1 (entget mt)))) 2)
          ); -
        ); sqrt
        3
      ); <
      (ssadd mt within3); then
    ); if
  ); repeat
  (sssetfirst nil within3); select/grip/highlight
  (princ)
); defun

 

 

It leaves the closer-than-3 Mtexts in the 'within3' selection set, to do with what you will, and selected/gripped/highlighted at the end.

 

It counts on some characteristics of the sample drawing -- that there is only one Polyline path in the drawing, it's at zero elevation, and that there are no Mtext objects close to the path that are not  such elevation markers.  If those might not always be true, the User could be asked to select the path, and the Mtexts could be filtered by Layer or something.

 

In the process, I found that most of your Mtexts are also duplicated, as many of the little crosses are, so I'd again suggest OVERKILL before running this.

Kent Cooper, AIA
0 Likes
Message 9 of 14

Oliver_C3D
Advocate
Advocate

Excellent and accurate
Exactly what was needed. Thank you very much for your time and effort @Kent1Cooper 

0 Likes
Message 10 of 14

Kent1Cooper
Consultant
Consultant

You're welcome.  Another thought -- if the path may be at an elevation other than 0, change this line:

(expt (distof (cdr (assoc 1 (entget mt)))) 2)

to this [I hope -- untested]:

(expt
  (-
    (distof (cdr (assoc 1 (entget mt)))); Mtext Elevation
    (cdr (assoc 38 (entget path))); path Elevation
  ); -
  2
); expt

 

Kent Cooper, AIA
0 Likes
Message 11 of 14

Oliver_C3D
Advocate
Advocate

Hello good time
Thank you for your efforts. Last night, I tested a build on my file and it ran. But I do not know why it gives an error in another file when running.

 
 
 
0 Likes
Message 12 of 14

Kent1Cooper
Consultant
Consultant

It's because the Mtext objects have font and color formatting  before they get to the elevation numerical content.  Pick one and look at the Contents in the Text category in its Properties.  That makes this:
  (distof (cdr (assoc 1 (entget mt))))

return nil [because (distof) can convert only purely distance-content text strings into numbers], instead of the number part, and the (expt) function that squares it fails because it can't square nil.

 

Look into either using plain Text for these things [and changing the (ssget) filter to look for that instead of Mtext], or using STRIPMTEXT on them [a free download you can Search for], or Exploding them all to remove the formatting and turn them into Text.  When I Explode the Mtexts and change the search filter:

....

    mss (ssget "_X" '((0 . "TEXT")))

....

it works for me.

Kent Cooper, AIA
0 Likes
Message 13 of 14

Oliver_C3D
Advocate
Advocate

Hello @Kent1Cooper 

0 Likes
Message 14 of 14

Kent1Cooper
Consultant
Consultant
Accepted solution

; error: function undefined for argument: -6.77774

 

I think that particular message means that the number is not something a function can work with, though of the right "class" of input [i.e. not like the error you get if, for example, you try to feed a text string to a numerical function].  Here, it has to be the (sqrt) function -- not being built to work with imaginary numbers, it can't find the square root of a negative number.  It takes the distance in XYZ terms from the Text insertion point to the closest point on the Polyline, which is the hypotenuse of a right triangle, and squares it, then squares the numerical equivalent of the text content, being [it thinks] the vertical leg of the triangle, and subtracts that from the hypotenuse squared, to get the square of Pythagoras's horizontal base leg of the triangle.  It then takes the square root of that to get the length of that base leg, i.e. the distance in XY terms only of the Text from the Polyline.

....

  (sqrt ; distance in XY only
    (- ;; to get the horizontal base leg of the right triangle, subtract:
      (expt
        (distance ; in 3D ;; length of the hypotenuse
          (setq ins (cdr (assoc 10 (entget mt))))
          (vlax-curve-getClosestPointTo path ins)
        ); distance
        2 ;; = squared
      ); expt
      (expt (distof (cdr (assoc 1 (entget mt)))) 2) ;; minus the squared vertical leg
    ); -
  ); sqrt

....

 

So what I think is happening is that in some instance, the square of the vertical leg is larger than the square of the hypotenuse!  How can that be, you ask?  The text contents are rounded to two decimal places, but they actually mostly have more.  If one is close enough to the path in XY terms [so the vertical leg is very close to the length of the hypotenuse], and its rounding went up from the true distance by enough, that could send its squared value over that of the hypotenuse.

 

The code was originally written to convert the text content to a number, because at the time I didn't realize the Text/Mtext objects were actually at their elevations, not at the elevation of the Polyline.  SO:  Instead, let's use their actual un-rounded vertical position:

 

(defun C:Within3 (/ mss path n mt ins)
  (setq
    mss (ssget "_X" '((0 . "TEXT")))
    path (ssname (ssget "_X" '((0 . "LWPOLYLINE"))) 0)
    within3 (ssadd); initially empty
  ); setq
  (repeat (setq n (sslength Mss))
    (setq mt (ssname mss (setq n (1- n))))
    (if
      (<
        (sqrt ; distance in XY only
          (-
            (expt
              (distance ; in 3D
                (setq ins (cdr (assoc 10 (entget mt))))
                (vlax-curve-getClosestPointTo path ins)
              ); distance
              2
            ); expt
            ;; (expt (distof (cdr (assoc 1 (entget mt)))) 2); converted text content
            (expt (caddr ins) 2); actual elevation, not text content
          ); -
        ); sqrt
        3
      ); <
      (ssadd mt within3); then
    ); if
  ); repeat
  (sssetfirst nil within3); select/grip/highlight
  (princ)
); defun

 

That worked for me in your drawing.

Kent Cooper, AIA
0 Likes