Gap detection script is marking coincident points

Gap detection script is marking coincident points

nbonnett-murphy
Advocate Advocate
573 Views
7 Replies
Message 1 of 8

Gap detection script is marking coincident points

nbonnett-murphy
Advocate
Advocate

Can anyone help me troubleshoot the script found here?: https://autocadtips1.com/2011/10/28/autolisp-find-and-mark-gaps/

 

It works quite well, but frequently circles "gaps" that are coincident out to 8 digits. It seems to especially have a problem with arcs.

 

Here's an example:

nbonnettmurphy_0-1713979279908.png

 

The "boundary" command doesn't have an issue with that corner. Moving the line endpoint handle, then resnapping it to the other line doesn't help, the script still circles it as a gap. Deleting one line and redrawing does work, the script won't mark the corner after that. The file is exported from an inventor flat pattern; I'd expect z-axis misalignment except that the problem happens on arbitrary sides of the slots. Plus you'd see doubled lines in that case.

 

I've done a little bit of testing, but I'm not very familiar with LISP and unfortunately I don't have a couple days to devote to it right now. Hopefully someone more experienced will be able to see the problem.

 

I've attached the example file below.

I did change the default gapsize, fluff, and circle size variables, as well as increased the rtos precision in a couple places to try and troubleshoot. Here is my modified script:

;| GAP.LSP locates and marks the ends of arcs, lines, and plines that are close
but not exactly coincident. Gaps are marked by drawing circles on the GAP layer.
You can select part of a drawing to check or press ENTER to check the whole drawing.
These are the distances to control how the gaps are located
Gap Limit = Gaps less than this, but more than fluff are marked
Fluff = Gaps less than this are not marked
Circle Size = Size of circle to mark gaps with
Original routine by McNeel & Associates
-----------------------------------------------------------------------
-----------------------------------------------------------------------
Modified by J. Tippit, SPAUG President 12/29/98
E-mail: cadpres@spaug.org
Web Site: http://www.spaug.org
-----------------------------------------------------------------------
-----------------------------------------------------------------------
Revisions:
12/29/98 Added ability to change Gap Limit, Fluff, & Circle Size
Added CMDECHO, UNDO, OSMODE, & CURLAY
Added a counter for the number of cicles that are drawn
and other misc. prompts
Changed the Gap layer to be RED
-----------------------------------------------------------------------
|;
(defun dxf (x e) (cdr (assoc x e)))
; Removes entities other than line, pline, arc from a selection set
(defun checkss (ss / i)
(setq i (sslength ss))
(while (> i 0)
(setq i (1- i))
(setq ent (entget (ssname ss i)))
(or
(= "LINE" (dxf 0 ent))
(= "POLYLINE" (dxf 0 ent))
(= "ARC" (dxf 0 ent))
(ssdel (ssname ss i) ss)
)
)
(if (> (sslength ss) 0)
ss
)
)
; Returns the endpoints of lines, arcs and pines
(defun endsofent (ent / v e1 e2)
(cond
((= "LINE" (dxf 0 ent))
(list (dxf 10 ent) (dxf 11 ent))
)
((= "ARC" (dxf 0 ent))
(list
(polar (dxf 10 ent) (dxf 50 ent) (dxf 40 ent))
(polar (dxf 10 ent) (dxf 51 ent) (dxf 40 ent))
)
)
((= "POLYLINE" (dxf 0 ent))
(setq v (entget (entnext (dxf -1 ent))))
(setq e1 (dxf 10 v))
(while (/= "SEQEND" (dxf 0 v))
(setq e2 (dxf 10 v))
(setq v (entget (entnext (dxf -1 v))))
)
(list e1 e2)
)
)
)
; gets a selection set of all entities near a point
(defun ssat (pt dist)
(ssget "c"
(list (- (car pt) dist) (- (cadr pt) dist))
(list (+ (car pt) dist) (+ (cadr pt) dist))
)
)
; Looks through a selection set and finds ends near but not at ends
; of other entities
(defun markgaps (ss / i ends)
(setq i (sslength ss))
(while (> i 0)
(setq i (1- i))
(setq ent (entget (ssname ss i)))
(setq ends (endsofent ent))
; (princ ".")
; (princ "\n")
; (princ (car ends))
; (princ " -- ")
; (princ (cadr ends))
(endsnear (car ends) gaplimit)
(endsnear (cadr ends) gaplimit)
)
)
(defun circle (pt r)
(command "circle" pt r)
(if (= CNT nil)
(setq CNT 1)
(setq CNT (1+ CNT))
)
)
; Finds the entities near a point and marks their ends if they
; are also near the point
(defun endsnear ( pt dist / ent ends)
(if (setq sse (ssat pt dist))
(progn
(setq j (sslength sse))
(while (> j 0)
(setq j (1- j))
(setq ent (entget (ssname sse j)))
(if
(setq ends (endsofent ent))
(progn
(setq d (distance (car ends) pt))
(if (< 0.0 d gaplimit)
(circle pt circlesize)
)
(setq d (distance (cadr ends) pt))
(if (< 0.0 d gaplimit)
(circle pt circlesize)
)
)
)
)
)
)
)
; Main control function
(defun c:GAP ( / ss )
(setvar "cmdecho" 0)
(command "._undo" "be")
(setq #OSMOD (getvar "osmode"))
(setvar "osmode" 0)
(setq #CURLA (getvar "clayer"))
(setq CNT nil)
(if (= gaplimit nil)
(or
(setq gaplimit (getdist "\nSet Gap Limit <0.0156>: "))
(setq gaplimit 0.0156)
)
(progn
(setq gaplimit2 gaplimit)
(or
(setq gaplimit (getdist (strcat "\nSet Gap Limit <" (rtos gaplimit 2 5) ">: ")))
(setq gaplimit gaplimit2)
)
)
)
(if (= fluff nil)
(or
(setq fluff (getdist "\nSet Fluff <0.00001>: "))
(setq fluff 0.00001)
)
(progn
(setq fluff2 fluff)
(or
(setq fluff (getdist (strcat "\nSet Fluff <" (rtos fluff 2 5) ">: ")))
(setq fluff fluff2)
)
)
)
(if (= circlesize nil)
(or
(setq circlesize (getdist "\nSet Circle Size <0.1>: "))
(setq circlesize 0.1)
)
(progn
(setq circlesize2 circlesize)
(or
(setq circlesize (getdist (strcat "\nSet Circle Size <" (rtos circlesize 2 3) ">: ")))
(setq circlesize circlesize2)
)
)
)
(command "._layer" "m" "GAP" "c" "1" "GAP" "")
(princ "\nSelect objects or <ENTER> for all: ")
(or
(and
(setq ss (ssget))
(setq ss (checkss ss))
)
(setq ss (ssget "x"
'((-4 . "<OR")
(0 . "LINE")
(0 . "ARC")
(0 . "POLYLINE")
(-4 . "OR>")
)
)
)
)
(princ "\nChecking for Gaps - please wait")
(markgaps ss)
(princ "done!")
(if (/= CNT nil)
(princ (strcat "\n" (itoa CNT) " Circles drawn."))
(princ "\nNo Gaps found.")
)
(setvar "clayer" #CURLA)
(setvar "osmode" #OSMOD)
(command "._undo" "e")
(setvar "cmdecho" 1)
(princ)
)
(prompt "\nLOCATE GAPS is loaded... type GAP to start!")
(princ)

Also, I know there are a lot of clever ways to find and deal with gaps. This script would just be really convenient if it worked as expected.

0 Likes
Accepted solutions (1)
574 Views
7 Replies
Replies (7)
Message 2 of 8

nbonnett-murphy
Advocate
Advocate

Side note: The script is almost 30 years old, I'm impressed it functions at all 🙂

0 Likes
Message 3 of 8

Kent1Cooper
Consultant
Consultant

@nbonnett-murphy wrote:

.... The file is exported from an inventor flat pattern; I'd expect z-axis misalignment except that the problem happens on arbitrary sides of the slots. Plus you'd see doubled lines in that case.

....

Side note: The script is almost 30 years old, I'm impressed it functions at all


I don't understand your reasoning about Z-axis misalignment.  [What are "the slots"?  And why couldn't things be mismatched in Z coordinate without doubling of anything?]

 

The age of the routine may explain why it allows POLYLINEs but not LWPOLYLINEs [which may not have existed at the time], which I assume you should add to the allowable object types.

 

There are various suggestions I could make to simplify it, but first....

 

EDIT:  I find that if I use FLATTEN on it, no gaps are detected using its default values.  That suggests Z differences, however small.

Kent Cooper, AIA
Message 4 of 8

nbonnett-murphy
Advocate
Advocate

If the flat pattern view inside inventor isn't perfectly normal to the screen you'll see nearly doubled lines in the dwg (front and back surfaces of the part), but only on the edges that are inclined towards you. The script shows gaps on arbitrary sides of the slot cutouts rather than one side consistently, so I assumed that meant the view from inventor wasn't misaligned.

 

I clearly don't know much about how autocad handles 3d, I forget that it's a feature until I accidentally rotate the model.

 

Thanks for the tip on flatten. If I use flatten with "remove hidden", I'm actually able to see the gaps it's complaining about:

nbonnettmurphy_0-1713991340482.png

Prior to flattening, the line and arc connected visually and had coincident start/end coordinates, including the Z coords. This is progress for sure, but now I'm even more confused. Were there doubled lines on different Z levels? Why couldn't I see or select them before flattening? Before flattening, I can select all of the lines in the drawing and see via prop that the start and end Z coords are all 0.0. The upper right gap in this screenshot measures 0.0112, which I definitely would have been able to identify visually before flattening. The border after flattening looks to me like the interpolation errors you get when you export without a high enough quality setting.

 

Am I actually working in 3d here without knowing it? I tried doing CHANGE>ELEVATION to 0 on all, but it didn't seem to have any effect.

 

You must be right that there are some Z coord shenanigans from the inventor output, but I don't have the autocad 3D experience to even know where to start looking. The script is so old that it might not be worth either of our time to rework it. There might not even have been a 3rd dimension in '98.

 

I'll probably just use pedit/join to scrub things coming out of inventor to make sure our nesting software can handle it.

0 Likes
Message 5 of 8

komondormrex
Mentor
Mentor
Accepted solution

there was an error in the code, since the fluff is constantly set to zero in the endnear subfunction. should go fluff instead.

;Gets the value of the x sublist in the e assoc list
(defun dxf (x e) (cdr (assoc x e)))

; Removes entities other than line, pline, arc from a selection set
(defun checkss (ss / i)
	(setq i (sslength ss))
	(while (> i 0)
		(setq i (1- i))
		(setq ent (entget (ssname ss i)))
		(or
			(= "LINE" (dxf 0 ent))
			(= "POLYLINE" (dxf 0 ent))
			(= "ARC" (dxf 0 ent))
			(ssdel (ssname ss i) ss)
		)
	)
	(if (> (sslength ss) 0)
	ss
	)
)

; Returns the endpoints of lines, arcs and pines
(defun endsofent (ent / v e1 e2)
	(cond
		((= "LINE" (dxf 0 ent))
			(list (dxf 10 ent) (dxf 11 ent))
		)
		((= "ARC" (dxf 0 ent))
			(list
			(polar (dxf 10 ent) (dxf 50 ent) (dxf 40 ent))
			(polar (dxf 10 ent) (dxf 51 ent) (dxf 40 ent))
			)
		)
		((= "POLYLINE" (dxf 0 ent))
			(setq v (entget (entnext (dxf -1 ent))))
			(setq e1 (dxf 10 v))
			(while (/= "SEQEND" (dxf 0 v))
			(setq e2 (dxf 10 v))
			(setq v (entget (entnext (dxf -1 v))))
			)
			(list e1 e2)
		)
	)
)

; gets a selection set of all entities near a point
	(defun ssat (pt dist)
	(ssget "c"
	(list (- (car pt) dist) (- (cadr pt) dist))
	(list (+ (car pt) dist) (+ (cadr pt) dist))
	)
)
; Looks through a selection set and finds ends near but not at ends
; of other entities

(defun markgaps (ss / i ends)
	(setq i (sslength ss))
	(while (> i 0)
		(setq i (1- i))
		(setq ent (entget (ssname ss i)))
		(setq ends (endsofent ent))
		; (princ ".")
		; (princ "\n")
		; (princ (car ends))
		; (princ " -- ")
		; (princ (cadr ends))
		(endsnear (car ends) gaplimit)
		(endsnear (cadr ends) gaplimit)
	)
)

(defun circle (pt r)
	(command "circle" pt r)
	(if (= CNT nil)
	(setq CNT 1)
	(setq CNT (1+ CNT))
	)
)

; Finds the entities near a point and marks their ends if they
; are also near the point
(defun endsnear ( pt dist / ent ends)
	(if (setq sse (ssat pt dist))
		(progn
			(setq j (sslength sse))
			(while (> j 0)
				(setq j (1- j))
				(setq ent (entget (ssname sse j)))
				(if
					(setq ends (endsofent ent))
					(progn
						(setq d (distance (car ends) pt))
						(if (< fluff d gaplimit)
							(circle pt circlesize)
						)
						(setq d (distance (cadr ends) pt))
						(if (< fluff d gaplimit)
							(circle pt circlesize)
						)
					)
				)
			)
		)
	)
)

; Main control function
(defun c:GAP ( / ss )
	(setvar "cmdecho" 0)
	(command "._undo" "be")
	(setq #OSMOD (getvar "osmode"))
	(setvar "osmode" 0)
	(setq #CURLA (getvar "clayer"))
	(setq CNT nil)
	(if (= gaplimit nil)
		(or
			(setq gaplimit (getdist "\nSet Gap Limit <0.0156>: "))
			(setq gaplimit 0.0156)
		)
		(progn
			(setq gaplimit2 gaplimit)
			(or
				(setq gaplimit (getdist (strcat "\nSet Gap Limit <" (rtos gaplimit 2 5) ">: ")))
				(setq gaplimit gaplimit2)
			)
		)
	)
	(if (= fluff nil)
		(or
			(setq fluff (getdist "\nSet Fluff <0.00001>: "))
			(setq fluff 0.00001)
		)
		(progn
			(setq fluff2 fluff)
			(or
				(setq fluff (getdist (strcat "\nSet Fluff <" (rtos fluff 2 5) ">: ")))
				(setq fluff fluff2)
			)
		)
	)
	(if (= circlesize nil)
		(or
			(setq circlesize (getdist "\nSet Circle Size <0.1>: "))
			(setq circlesize 0.1)
		)
		(progn
			(setq circlesize2 circlesize)
			(or
				(setq circlesize (getdist (strcat "\nSet Circle Size <" (rtos circlesize 2 3) ">: ")))
				(setq circlesize circlesize2)
			)
		)
	)
	(command "._layer" "m" "GAP" "c" "1" "GAP" "")
	(princ "\nSelect objects or <ENTER> for all: ")
	(or
		(and
			(setq ss (ssget))
			(setq ss (checkss ss))
		)
		(setq ss (ssget "x"
					'((-4 . "<OR")
						(0 . "LINE")
		   				(0 . "ARC")
						(0 . "POLYLINE")
					  (-4 . "OR>")
					 )
				 )
		)
	)
	(princ "\nChecking for Gaps - please wait")
	(markgaps ss)
	(princ "done!")
	(if (/= CNT nil)
		(princ (strcat "\n" (itoa CNT) " Circles drawn."))
		(princ "\nNo Gaps found.")
	)
	(setvar "clayer" #CURLA)
	(setvar "osmode" #OSMOD)
	(command "._undo" "e")
	(setvar "cmdecho" 1)
	(princ)
)
(prompt "\nLOCATE GAPS is loaded... type GAP to start!")
(princ)

 

Message 6 of 8

nbonnett-murphy
Advocate
Advocate

Wow thanks! That seems to have fixed it pending further testing.

 

So basically it had no lower limit correct? Do you think the reason it was finding gaps is because the points were on different coordinates out at #e-15 or something along those lines?

0 Likes
Message 7 of 8

nbonnett-murphy
Advocate
Advocate

Thanks again, this will save us a ton of time. 

 

Here is what I finally ended up with. I changed the default limits again, changed the "fluff" variable name to something more descriptive, and moved the osnap toggle after the prompts so that it doesn't wipe out your osnap settings if you ESC out halfway through.

 

;Gets the value of the x sublist in the e assoc list
(defun dxf (x e) (cdr (assoc x e)))

; Removes entities other than line, pline, arc from a selection set
(defun checkss (ss / i)
	(setq i (sslength ss))
	(while (> i 0)
		(setq i (1- i))
		(setq ent (entget (ssname ss i)))
		(or
			(= "LINE" (dxf 0 ent))
			(= "POLYLINE" (dxf 0 ent))
			(= "ARC" (dxf 0 ent))
			(ssdel (ssname ss i) ss)
		)
	)
	(if (> (sslength ss) 0)
	ss
	)
)

; Returns the endpoints of lines, arcs and pines
(defun endsofent (ent / v e1 e2)
	(cond
		((= "LINE" (dxf 0 ent))
			(list (dxf 10 ent) (dxf 11 ent))
		)
		((= "ARC" (dxf 0 ent))
			(list
			(polar (dxf 10 ent) (dxf 50 ent) (dxf 40 ent))
			(polar (dxf 10 ent) (dxf 51 ent) (dxf 40 ent))
			)
		)
		((= "POLYLINE" (dxf 0 ent))
			(setq v (entget (entnext (dxf -1 ent))))
			(setq e1 (dxf 10 v))
			(while (/= "SEQEND" (dxf 0 v))
			(setq e2 (dxf 10 v))
			(setq v (entget (entnext (dxf -1 v))))
			)
			(list e1 e2)
		)
	)
)

; gets a selection set of all entities near a point
	(defun ssat (pt dist)
	(ssget "c"
	(list (- (car pt) dist) (- (cadr pt) dist))
	(list (+ (car pt) dist) (+ (cadr pt) dist))
	)
)
; Looks through a selection set and finds ends near but not at ends
; of other entities

(defun markgaps (ss / i ends)
	(setq i (sslength ss))
	(while (> i 0)
		(setq i (1- i))
		(setq ent (entget (ssname ss i)))
		(setq ends (endsofent ent))
		; (princ ".")
		; (princ "\n")
		; (princ (car ends))
		; (princ " -- ")
		; (princ (cadr ends))
		(endsnear (car ends) gaplimit)
		(endsnear (cadr ends) gaplimit)
	)
)

(defun circle (pt r)
	(command "circle" pt r)
	(if (= CNT nil)
	(setq CNT 1)
	(setq CNT (1+ CNT))
	)
)

; Finds the entities near a point and marks their ends if they
; are also near the point
(defun endsnear ( pt dist / ent ends)
	(if (setq sse (ssat pt dist))
		(progn
			(setq j (sslength sse))
			(while (> j 0)
				(setq j (1- j))
				(setq ent (entget (ssname sse j)))
				(if
					(setq ends (endsofent ent))
					(progn
						(setq d (distance (car ends) pt))
						(if (< lowerLimit d gaplimit)
							(circle pt circlesize)
						)
						(setq d (distance (cadr ends) pt))
						(if (< lowerLimit d gaplimit)
							(circle pt circlesize)
						)
					)
				)
			)
		)
	)
)

; Main control function
(defun c:GAP ( / ss )
	(setvar "cmdecho" 0)
	(command "._undo" "be")
	(setq #OSMOD (getvar "osmode"))

	(setq #CURLA (getvar "clayer"))
	(setq CNT nil)
	(if (= gaplimit nil)
		(or
			(setq gaplimit (getdist "\nSet Upper Gap Limit <0.125>: "))
			(setq gaplimit 0.125)
		)
		(progn
			(setq gaplimit2 gaplimit)
			(or
				(setq gaplimit (getdist (strcat "\nSet Upper Gap Limit <" (rtos gaplimit 2 5) ">: ")))
				(setq gaplimit gaplimit2)
			)
		)
	)
	(if (= lowerLimit nil)
		(or
			(setq lowerLimit (getdist "\nSet Lower Limit <0.00001>: "))
			(setq lowerLimit 0.00001)
		)
		(progn
			(setq lowerLimit2 lowerLimit)
			(or
				(setq lowerLimit (getdist (strcat "\nSet Lower Limit <" (rtos lowerLimit 2 5) ">: ")))
				(setq lowerLimit lowerLimit2)
			)
		)
	)
	(if (= circlesize nil)
		(or
			(setq circlesize (getdist "\nSet Circle Size <0.1>: "))
			(setq circlesize 0.1)
		)
		(progn
			(setq circlesize2 circlesize)
			(or
				(setq circlesize (getdist (strcat "\nSet Circle Size <" (rtos circlesize 2 3) ">: ")))
				(setq circlesize circlesize2)
			)
		)
	)
	(command "._layer" "m" "GAP" "c" "1" "GAP" "")
	(princ "\nSelect objects or <ENTER> for all: ")
	(or
		(and
			(setq ss (ssget))
			(setq ss (checkss ss))
		)
		(setq ss (ssget "x"
					'((-4 . "<OR")
						(0 . "LINE")
		   				(0 . "ARC")
						(0 . "POLYLINE")
					  (-4 . "OR>")
					 )
				 )
		)
	)
	(princ "\nChecking for Gaps - please wait")
	(setvar "osmode" 0)
	(markgaps ss)
	(princ "\ndone!")
	(if (/= CNT nil)
		(princ (strcat "\n" (itoa CNT) " Circles drawn."))
		(princ "\nNo Gaps found.")
	)
	(setvar "clayer" #CURLA)
	(setvar "osmode" #OSMOD)
	(command "._undo" "e")
	(setvar "cmdecho" 1)
	(princ)
)
(prompt "\nLOCATE GAPS is loaded...... type GAP to start.")
(princ)
Message 8 of 8

komondormrex
Mentor
Mentor

you are welcome)

0 Likes