To Find Self Intersection of LWPOLYLINE

To Find Self Intersection of LWPOLYLINE

MehtaRajesh
Advocate Advocate
11,961 Views
123 Replies
Message 1 of 124

To Find Self Intersection of LWPOLYLINE

MehtaRajesh
Advocate
Advocate

Hi,

I am posting useful function to find Self Intersection for selected LWPOLYLINE.
You might be having other options, but I find this one as a quickest and Simple


(defun IsSelfIntersect (l / vcnt vcnt1 crossresult pt1 pt2 pt3 pt4)
(setq vcnt 0)
(setq crossresult F)
(repeat (1- (length l))
 (setq pt1 (nth vcnt l))
   (setq pt2 (nth (1+ vcnt) l))
   (setq vcnt1 vcnt)
 (setq isdone "T")
 (while isdone
  (if (and (nth (+ 2 vcnt) l) (nth (+ 3 vcnt) l))
  (progn
   (setq pt3 (nth (+ 2 vcnt) l))
   (setq pt4 (nth (+ 3 vcnt) l))
       (if (inters pt1 pt2 pt3 pt4)
   (progn
      (setq crossresult T)
   );progn
   );if
  );progn
  (progn
   (setq isdone nil)
  );progn
  );if
    (setq vcnt (1+ vcnt))
 );while
   (setq vcnt (1+ vcnt1)) 
);repeat
crossresult
);defun

Below Function to get the Coordinate list of LWPOLYLINE

(defun lwptslw (lst / pair rtn)
  (while (setq pair (assoc 10 lst))
    (setq rtn (cons (cdr pair) rtn)
   lst (cdr (member pair lst))
    )
  )
  (reverse rtn)
)

USAGE:
Command: (IsSelfIntersect (LWPTSLW (ENTGET (CAR (ENTSEL "\nSelect Lwpolyline to find Self Intersectioni")))))

Regards,
Rajesh

0 Likes
11,962 Views
123 Replies
Replies (123)
Message 2 of 124

Kent1Cooper
Consultant
Consultant

@MehtaRajesh wrote:

....
I am posting useful function to find Self Intersection for selected LWPOLYLINE.
You might be having other options....

 

Below Function to get the Coordinate list of LWPOLYLINE
....


The (inters) approach in yours [which considers only the straight-line directions between designated points] would miss intersecting arc segments, such as a situation like this:

 

PLSelfInt.png

 

Here's a way to test for self-intersection that will catch those.  It's worth reading other posts on this thread that it comes from, for various considerations, other attempts and their shortcomings, remaining limitations of this one, etc.

 

 

;; Function name: PSC = Polyline Self-Crossing
;; To determine whether a Polyline of any type Crosses itSelf.
;; With 3D Polylines, must have true intersection in 3D, not apparent in 2D.
;; Returns T if self-crossing, nil if not.
(defun PSC (poly / pltyp plobj plverts plints)
  (setq
    pltyp (cdr (assoc 0 (entget poly)))
    plobj (vlax-ename->vla-object poly)
    plverts (length (safearray-value (variant-value (vla-get-Coordinates plobj))))
    plints (/ (length (safearray-value (variant-value (vla-intersectwith plobj plobj acExtendNone)))) 3)
  ); end setq
  (setq plverts (/ plverts (if (= pltyp "LWPOLYLINE") 2 3)))
  (if (vlax-curve-isClosed poly)
    (< plverts plints); then - closed
    (if (equal (vlax-curve-getStartPoint poly) (vlax-curve-getEndPoint poly) 1e-8); else - open
      (<= plverts plints); then - start/end at same place
      (<= plverts (1+ plints)); else - open
    ); if
  ); if
); defun
(vl-load-com)

Usage:  (PSC PolylineEntityName)

 

And although the above doesn't use this method to get them, here's a more concise way to get the Coordinates list of LWPolyline vertices [with its entity-data list as the 'lst' argument], directly, without the need for variables or (cons) or (reverse) functions:

(defun lwpverts (lst)
  (mapcar 'cdr (vl-remove-if-not '(lambda (x) (= (car x) 10)) lst))
)

Kent Cooper, AIA
Message 3 of 124

Anonymous
Not applicable

 

Hello Kent

 

I am looking for a routine to detect (get them as a selection at the end of the routine)

from a classic ACAD selection of MANY 2D Plines and 2D LWPlines ?

 

Please is it possible to have this routine that will use your beautiful code ?

 

If ALL 2D Plines and 2D LWPlines have NO SelfIntersect, the current selection will be empty at the end ...

 

Regards, Patrice

 

0 Likes
Message 4 of 124

Kent1Cooper
Consultant
Consultant

patrice.braud wrote:

.... 

I am looking for a routine to detect (get them as a selection at the end of the routine)

from a classic ACAD selection of MANY 2D Plines and 2D LWPlines ?

.... 

If ALL 2D Plines and 2D LWPlines have NO SelfIntersect, the current selection will be empty at the end ...

.... 


Welcome to these Forums!  [Any relation to @braudpat?]

 

Certainly that can be done.  In barest terms, something like this [untested]:

 

(setq

  PSS (ssget '((0 . "*POLYLINE"))); Polyline Selection Set

  SIPSS (ssadd); initially empty Self-Intersecting Polyline Selection Set

); setq

(if PSS

  (repeat (setq n (sslength PSS))

    (setq PL (ssname PSS (setq n (1- n))))

    (if (PSC PL) (ssadd PL SIPSS)); if it's self-intersecting, put it into SIPSS

  ); repeat

); if

 

The SIPSS selection set should then have any self-intersecting Polylines from the selection in it, or be empty [but not nil] if none were found.

 

Or, if you want to just remove those that don't intersect themselves from the original set, rather than make a different set:

(setq PSS (ssget '((0 . "*POLYLINE")))); Polyline Selection Set

(if PSS

  (repeat (setq n (sslength PSS))

    (setq PL (ssname PSS (setq n (1- n))))

    (if (not (PSC PL)) (ssdel PL SIPSS)); if it's not self-intersecting, remove it from PSS

  ); repeat

); if

Kent Cooper, AIA
Message 5 of 124

braudpat
Mentor
Mentor

 

Hello Kent

 

1) The most important : THANKS !

 

2) You are Mr Sherlock HOLMES : of course I am "braudpat" !

 

3) SORRY I was walking through Forums with the wrong Login ...

 

Regards, Patrice

 

Patrice ( Supporting Troops ) - Autodesk Expert Elite
If you are happy with my answer please mark "Accept as Solution" and if very happy please give me a Kudos (Felicitations) - Thanks

Patrice BRAUD

EESignature


0 Likes
Message 6 of 124

bergerF5EFQ
Participant
Participant

Unfortunately this code can't detect some simple self-intersecting polylines even with straight line segments only. Just do a test with a polyline representing "the house of nikolaus" (https://de.wikipedia.org/wiki/Haus_vom_Nikolaus)

 

0 Likes
Message 7 of 124

Kent1Cooper
Consultant
Consultant

@bergerF5EFQ wrote:

Unfortunately this code can't detect some simple self-intersecting polylines ....


That has to do with self-intersecting points being at vertices.  See the last sentence before the code in Message 2 -- that is specifically one of the shortcomings discussed in the linked topic.

 

Whether that can be accounted for in a way that would cover all possibilities, I'm not sure.  Going through the vertices to see whether there are any repeats would catch those self-intersections.  But it won't be enough -- the test fails for even a self-intersection by a vertex lying somewhere along another segment but not at another vertex:

Kent1Cooper_0-1717887534472.png

Kent Cooper, AIA
Message 8 of 124

daniel_cadext
Advisor
Advisor

If you define intersecting as any two segments that share a point, then all polylines are self-intersecting. You can iterate the segments and remove where the end is the same as the beginning of the next, but how about where the beginning and end coincide?

 

import traceback
from pyrx_imp import Rx, Ge, Gi, Db, Ap, Ed

def PyRxCmd_doit() -> None:
    try:
        es = Ed.Editor.entSel("\nSelect: ", Db.Polyline.desc())
        tol = Ge.Tol.current().equalPoint()
        pl = Db.Polyline(es[1])
        cl = pl.getAcGeCurve().explode()
        cllen = len(cl)
        
        for lidx in range(cllen):
            for ridx in range(lidx + 1, cllen):
                cci = Ge.CurveCurveInt3d(cl[lidx], cl[ridx])
                for i in range(cci.numIntPoints()):
                    prs = cci.getIntParams(i)
                    print(prs)
                    if prs[1] < tol:
                        continue
                    if prs == (1.0, 0.0):# or (0.0, 1.0)?
                        continue
                    Ed.Core.grDrawCircle(cci.intPoint(i), 5, 20, 3)

    except Exception as err:
        traceback.print_exception(err)

  

inter.png

Python for AutoCAD, Python wrappers for ARX https://github.com/CEXT-Dan/PyRx
0 Likes
Message 9 of 124

john.uhden
Mentor
Mentor

I may be out of line, but wouldn't (vla-intersectwith obj obj) return the self-intersecting coordinates, excluding corners?

John F. Uhden

0 Likes
Message 10 of 124

bergerF5EFQ
Participant
Participant

No, this wouldn't work, even when you apply the function correctly (vla-intersectwith obj obj acExtendNone).

 

But intersectwith would enable a brute force solution: create a copy of the polyline, explode it, and test each single segment against all others with (vla-intersectwith obj1 obj2 acExtendNone).

 

Another rather fast solution for polylines with straight segments only, or for a list of vertex points: calculate the sum of all angles between joining segments including the last one back to the first. The angles would sum up to exactly 2PI when the polyline vertices are in anti-clockwise direction, sum up to -2PI when the direction is clockwise, and sum up to 0 when there is one intersection.

But there is a problem when you have two intersections and therefore 3 loops inside the polyline: then the angle sum will be again 2PI or -2PI. You therefore need to count positive and negative angles separately, and only when there is either only one positive or only ne negative solution then there is no intersection.

 

0 Likes
Message 11 of 124

Kent1Cooper
Consultant
Consultant

@john.uhden wrote:

... wouldn't (vla-intersectwith obj obj) return the self-intersecting coordinates, excluding corners?


No [easy enough to try], it includes the corners, as "intersections" of segments.  That's exactly how the code in Message 2 works: it compares the number of vertices with the number of intersections that (intersectwith) returns.  The latter number includes everything in the former number plus any self-intersections that do not coincide with vertices, so that result would be larger if there are self-intersections.  But it counts any particular location only once, even if more than one thing happens there, which is why it can't be relied on if vertices coincide, or if a vertex lies somewhere along another segment.

Kent Cooper, AIA
Message 12 of 124

АлексЮстасу
Advisor
Advisor

Hi, @Kent1Cooper 

 

I found this your solution for finding self-crossing.
Tried it on several self-crossing polylines - works well.
Then I decided to draw a typical house as a joke, but your function for it returns nil, no self-crossing...

 

I looked at some more model self-crossing polylines. PSC often returns nil for self-crossing polylines with the Closed property. But not always.


-- Alexander, private person, pacifist, english only with translator 🙂 --

Object-modeling _ odclass-odedit.com _ Help

0 Likes
Message 13 of 124

АлексЮстасу
Advisor
Advisor

Um... Probably, cases with "houses" can not be considered cases of self-crossing?.. Um...

 

Attached his other simplest example.
PSC returns T if this polyline is not Closed. And returns nil if Closed.

PSC.png


-- Alexander, private person, pacifist, english only with translator 🙂 --

Object-modeling _ odclass-odedit.com _ Help

0 Likes
Message 14 of 124

john.uhden
Mentor
Mentor

@АлексЮстасу ,

Without having looked at Kent's, I wanted to try this on my own.

Please note that only polylines that have 1 or more segments crossing between endpoints are considered self-intersecting, closed or not.

 

(defun self-inters (e / ent pts obj1 obj2 ints)
  ;; John F. Uhden (11-11-2024)
  ;; Returns T for true and nil for false
  (and
    (setq ent (entget e))
    (or
      (= (cdr (assoc 0 ent)) "LWPOLYLINE")
      (alert "Object selected is\nnot a polyline.")
    )
    (setq pts (mapcar 'cdr (vl-remove-if-not '(lambda (x)(= (car x) 10)) ent)))
    (setq obj1 (vlax-ename->vla-object e))
    (setq obj2 (vla-copy obj1))
    (setq ints (vlax-invoke obj1 'intersectwith obj2 0))
    (not (vla-delete obj2))
    (< (length pts) (/ (length ints) 3))
  )
)

 

 

John F. Uhden

Message 15 of 124

АлексЮстасу
Advisor
Advisor

@john.uhden 

Thank you! I will try your function.


I found a 2006 function that returns the coordinates of all self-intersections. But I haven't tested it much yet.
I don't know the author (published by BOZ), the function was in the Russian segment.

 

(defun Pline_SelfInters (Pline / VertList VlaPline AllPoints ListToPointList)
  (defun ListToPointList (Lst)
    (if Lst
     (cons (list (car Lst) (cadr Lst)) (ListToPointList (cdddr Lst)))
    )
  )
  (setq VertList (mapcar 'cdr (vl-remove-if-not (function (lambda (x) (= (car x) 10))) (entget Pline)))
    VlaPline (vlax-ename->vla-object Pline)
    AllPoints (ListToPointList (vlax-safearray->list (vlax-variant-value (vla-IntersectWith VlaPline VlaPline acExtendNone))))
  )
  (vl-remove nil
   (mapcar
    (function
      (lambda (Pnt)
    (if (vl-some
         (function
          (lambda (x) (equal Pnt x 0.00001))
         )
         VertList
        )
      nil
      Pnt
    )
      )
    )
    AllPoints
   )
  )
)

 


This function also does not show touches and duplicates.
In my opinion, touching and duplication are still cases of self-intersection.
Or it would be good to get information about ‘true’ self-intersections, touches and duplicates.
If need to check boundaries to create hatches or regions or mpolygons etc., need to know about touches and duplications.

 


-- Alexander, private person, pacifist, english only with translator 🙂 --

Object-modeling _ odclass-odedit.com _ Help

0 Likes
Message 16 of 124

john.uhden
Mentor
Mentor

@АлексЮстасу ,

Here's a more simplified version (tested):

(defun selfinters (e / pts obj ints)
  ;; John F. Uhden (11-13-2024)
  (and
    (setq pts (vl-remove-if-not '(lambda (x)(= (car x) 10)) (entget e)))
    (setq obj (vlax-ename->vla-object e))
    (setq ints (vlax-invoke obj 'intersectwith obj 0))
    (< (length pts) (/ (length ints) 3))
  )
)

John F. Uhden

Message 17 of 124

komondormrex
Mentor
Mentor

@john.uhden 

does not work on that particular open pline

 

komondormrex_0-1731565900392.png

 

0 Likes
Message 18 of 124

Kent1Cooper
Consultant
Consultant

@АлексЮстасу wrote:

....

PSC returns T if this polyline is not Closed. And returns nil if Closed.

PSC.png


For me, if it's drawn around to its beginning and then closed, for a zero-length closing segment, it returns nil.  But if it's closed in the normal way, without the zero-length closing segment, it returns T.  I'm sure the coincident vertices and zero-length segment [which intersects both the first and otherwise-last segments] are to blame.  No, it's not universally foolproof under all possible circumstances, but I hope is good for "typical" or "clean" scenarios -- see the Topic linked to in Message 2, which discusses such things.

Kent Cooper, AIA
0 Likes
Message 19 of 124

Kent1Cooper
Consultant
Consultant

@АлексЮстасу wrote:

.... Then I decided to draw a typical house as a joke, but your function for it returns nil, no self-crossing.....


That's specifically because of what is described in the last sentence in Message 11.

Kent Cooper, AIA
0 Likes
Message 20 of 124

john.uhden
Mentor
Mentor

@komondormrex ,

You were certainly correct.  So I studied it over and over again, adding red circles for vertices and green circles for ints.  I thought I had it tidied up until I made a simple single cross polyline, not closed, but the last segment drawn to the first vertex, thereby creating an extra vertex that screwed up the comparison with the number of self intersections.  So I removed the last vertex if it were about exactly the same as the first vertex.

 

First, here is the stock code without all the test circles.

(defun selfinters (e / pts obj ints item TF)
  ;; John F. Uhden (11-15-2024)
  ;; TF stands for True/False
  (and
    (setq pts (mapcar 'cdr (vl-remove-if-not '(lambda (x)(= (car x) 10)) (entget e))))
    (if (equal (car pts)(last pts) 1e-8)
      (setq pts (reverse (cdr (reverse pts))))
      1
    )
    (setq obj (vlax-ename->vla-object e))
    (setq ints (vlax-invoke obj 'intersectwith obj 0))
    (or (setq TF (< (length pts) (/ (length ints) 3))) 1)
  )
  TF
)

 Here is the test version that draws the red and green circles.

(defun selfinters-test (e / pts obj ints item TF)
  ;; John F. Uhden (11-15-2024)
  ;; TF stands for True/False
  (and
    (setq pts (mapcar 'cdr (vl-remove-if-not '(lambda (x)(= (car x) 10)) (entget e))))
(setvar "cecolor" "1")
(setvar "osmode" 0)
    (if (equal (car pts)(last pts) 1e-8)
      (setq pts (reverse (cdr (reverse pts))))
      1
    )
    (repeat (setq n (length pts))
      (setq p (nth (setq n (1- n)) pts))
      (vl-cmdf ".circle" p 0.2)
    )
    (setq obj (vlax-ename->vla-object e))
    (setq ints (vlax-invoke obj 'intersectwith obj 0))
(setvar "cecolor" "3")
    (or (setq TF (< (length pts) (/ (length ints) 3))) 1)
    (while ints
      (repeat 3 (setq item (cons (car ints) item) ints (cdr ints)))
      (vl-cmdf ".circle"  (reverse item) 0.3)
      (print (reverse item))
      (setq item nil)
    )
  )
  TF
)

I hope you find that it works.

John F. Uhden

0 Likes