CXZEROLENClean-upVersion 1.0.0Tested in AutoCAD
Find and delete zero-length lines and polylines
Finds lines, arcs and polylines that are too short to see and either selects them for you to inspect or deletes them.
How to use it
- Type CXZEROLEN.
- Enter the length under which an object counts as zero length.
- Choose Select to look at them or Delete to remove them.
Good to know
- Searches the current model or layout tab.
Load it in AutoCAD
- Save the file anywhere you like.
- Type APPLOAD, pick the file and choose Load.
- Type the command name: CXZEROLEN.
To have it at every start, add it to the Startup Suite in the same dialog, or load a whole folder at once with the optional loader. About the optional loader
Tested in AutoCAD (full version). Other programs that run AutoLISP may work, but they are not tested.
The code
Plain text, nothing hidden. Copy it into a file named cxzerolen.lsp, or download it.
;;; ============================================================================
;;; CXZEROLEN - Find and delete zero-length lines and polylines
;;; Version 1.0.0
;;; CADXChange LISP tools - https://cadxchange.net/resources/lisp/cxzerolen
;;; Tested in: AutoCAD (full version). Other programs that run AutoLISP may work but are not tested.
;;; Copyright (c) CADXChange. Free for personal and commercial use; do not resell or
;;; republish it as a collection. Terms: https://cadxchange.net/legal/lisp-terms
;;; ============================================================================
;; The lines, arcs and polylines in NAMES that are shorter than TOL. With DEL true they are erased as well.
;; Returns the entity names found.
(defun cx:zerolen-run (names tol del / out len)
(foreach e names
(setq len (cx:ent-length (entget e)))
(if (and len (/= (cx:dxf 0 (entget e)) "CIRCLE") (< len tol))
(setq out (cons e out))))
(setq out (reverse out))
(if del (foreach e out (entdel e)))
out)
(defun c:CXZEROLEN ( / *error* cx:sv ss tol found)
(setq *error* cx:err)
(setq ss (ssget "X" (list '(0 . "LINE,ARC,LWPOLYLINE") (cons 410 (getvar "CTAB")))))
(if ss
(progn
(setq tol (cx:ask-real "Objects shorter than this count as zero length" 0.0001))
(setq found (cx:zerolen-run (cx:ss->list ss) tol nil))
(cond
((null found) (princ "\nNo zero-length objects found."))
((= (cx:ask-kw (strcat (itoa (length found)) " found. Action") "Delete Select" "Select") "Delete")
(cx:start '(("CMDECHO" . 0)))
(cx:zerolen-run found tol T)
(princ (strcat "\n" (itoa (length found)) " object(s) deleted."))
(cx:end))
(T
(sssetfirst nil (cx:list->ss found))
(princ (strcat "\n" (itoa (length found)) " object(s) selected."))))))
(princ))
;;; ---- Shared helpers (used by the command above; identical in every CADXChange tool) ----
;; The vertices of an LWPOLYLINE entity list as ((x y bulge start-width end-width) ...).
(defun cx:lw-verts (ed / out cur)
(foreach g ed
(cond
((= (car g) 10)
(if cur (setq out (cons cur out)))
(setq cur (list (cadr g) (caddr g) 0.0 0.0 0.0)))
((and cur (= (car g) 40)) (setq cur (list (nth 0 cur) (nth 1 cur) (nth 2 cur) (cdr g) (nth 4 cur))))
((and cur (= (car g) 41)) (setq cur (list (nth 0 cur) (nth 1 cur) (nth 2 cur) (nth 3 cur) (cdr g))))
((and cur (= (car g) 42)) (setq cur (list (nth 0 cur) (nth 1 cur) (cdr g) (nth 3 cur) (nth 4 cur))))))
(if cur (setq out (cons cur out)))
(reverse out))
;; The arc a bulge makes between P1 and P2 as (center-x center-y radius start-angle sweep), or nil for a straight segment.
;; The sweep is signed: positive turns counter-clockwise.
(defun cx:bulge-arc (p1 p2 b / theta dx dy len nx ny d ccx ccy)
(if (< (abs b) 1e-9)
nil
(progn
(setq theta (* 4.0 (atan b))
dx (- (car p2) (car p1))
dy (- (cadr p2) (cadr p1))
len (sqrt (+ (* dx dx) (* dy dy))))
(if (< len 1e-12)
nil
(progn
(setq nx (/ (- dy) len)
ny (/ dx len)
d (/ (/ len 2.0) (/ (sin (/ theta 2.0)) (cos (/ theta 2.0))))
ccx (+ (/ (+ (car p1) (car p2)) 2.0) (* nx d))
ccy (+ (/ (+ (cadr p1) (cadr p2)) 2.0) (* ny d)))
(list ccx ccy (/ len (* 2.0 (sin (/ (abs theta) 2.0))))
(atan (- (cadr p1) ccy) (- (car p1) ccx)) theta))))))
;; The number of segments of a polyline with N vertices.
(defun cx:seg-count (n closed)
(if closed n (1- n)))
;; A polyline as a list of (x y) points; arcs are cut into short straight pieces.
(defun cx:lw-points (verts closed / out i n a v1 v2 k steps ang)
(setq i 0 n (length verts))
(repeat (cx:seg-count n closed)
(setq v1 (nth i verts)
v2 (nth (rem (1+ i) n) verts)
a (cx:bulge-arc v1 v2 (nth 2 v1))
out (cons (list (car v1) (cadr v1)) out))
(if a
(progn
(setq steps (max 4 (fix (/ (abs (nth 4 a)) 0.15))) k 1)
(while (< k steps)
(setq ang (+ (nth 3 a) (* (nth 4 a) (/ (float k) steps)))
out (cons (list (+ (car a) (* (nth 2 a) (cos ang))) (+ (cadr a) (* (nth 2 a) (sin ang)))) out)
k (1+ k)))))
(setq i (1+ i)))
(if (not closed)
(setq out (cons (list (car (last verts)) (cadr (last verts))) out)))
(reverse out))
(defun cx:dxf (code ed)
(cdr (assoc code ed)))
;; An object as a list of (x y) points; circles and ellipses are cut into 120 pieces, arcs into short pieces.
;; Returns nil for an object it does not know.
(defun cx:ent-points (ed / type ctr r a1 a2 k pts ratio ang maj steps)
(setq type (cx:dxf 0 ed))
(cond
((= type "LWPOLYLINE") (cx:lw-points (cx:lw-verts ed) (= 1 (logand 1 (cx:dxf 70 ed)))))
((= type "LINE")
(list (list (car (cx:dxf 10 ed)) (cadr (cx:dxf 10 ed))) (list (car (cx:dxf 11 ed)) (cadr (cx:dxf 11 ed)))))
((= type "CIRCLE")
(setq ctr (cx:dxf 10 ed) r (cx:dxf 40 ed) k 0)
(repeat 120
(setq ang (* 2.0 pi (/ k 120.0))
pts (cons (list (+ (car ctr) (* r (cos ang))) (+ (cadr ctr) (* r (sin ang)))) pts)
k (1+ k)))
(reverse pts))
((= type "ARC")
(setq ctr (cx:dxf 10 ed) r (cx:dxf 40 ed) a1 (cx:dxf 50 ed) a2 (cx:dxf 51 ed))
(if (<= a2 a1) (setq a2 (+ a2 (* 2.0 pi))))
(setq steps (max 8 (fix (/ (- a2 a1) 0.05))) k 0)
(repeat (1+ steps)
(setq ang (+ a1 (* (- a2 a1) (/ (float k) steps)))
pts (cons (list (+ (car ctr) (* r (cos ang))) (+ (cadr ctr) (* r (sin ang)))) pts)
k (1+ k)))
(reverse pts))
((= type "ELLIPSE")
(setq ctr (cx:dxf 10 ed) maj (cx:dxf 11 ed) ratio (cx:dxf 40 ed) a1 (cx:dxf 41 ed) a2 (cx:dxf 42 ed))
(if (<= a2 a1) (setq a2 (+ a2 (* 2.0 pi))))
(setq k 0)
(repeat 121
(setq ang (+ a1 (* (- a2 a1) (/ k 120.0)))
pts (cons (list (+ (car ctr) (* (car maj) (cos ang)) (* (- (cadr maj)) ratio (sin ang)))
(+ (cadr ctr) (* (cadr maj) (cos ang)) (* (car maj) ratio (sin ang))))
pts)
k (1+ k)))
(reverse pts))))
(defun cx:lw-length (verts closed / total i n a v1 v2)
(setq total 0.0 i 0 n (length verts))
(repeat (cx:seg-count n closed)
(setq v1 (nth i verts)
v2 (nth (rem (1+ i) n) verts)
a (cx:bulge-arc v1 v2 (nth 2 v1))
total (+ total (if a
(* (nth 2 a) (abs (nth 4 a)))
(distance (list (car v1) (cadr v1)) (list (car v2) (cadr v2))))))
(setq i (1+ i)))
total)
;; True for a closed polyline, a circle and a full ellipse.
(defun cx:ent-closed (ed / type)
(setq type (cx:dxf 0 ed))
(cond
((= type "LWPOLYLINE") (= 1 (logand 1 (cx:dxf 70 ed))))
((= type "CIRCLE") T)
((= type "ELLIPSE") (< (abs (- (- (cx:dxf 42 ed) (cx:dxf 41 ed)) (* 2.0 pi))) 1e-6))))
;; The length of a line, arc, polyline, circle (its circumference) or ellipse; nil for other objects.
(defun cx:ent-length (ed / type pts total i s)
(setq type (cx:dxf 0 ed))
(cond
((= type "LINE") (distance (cx:dxf 10 ed) (cx:dxf 11 ed)))
((= type "CIRCLE") (* 2.0 pi (cx:dxf 40 ed)))
((= type "ARC")
(setq s (- (cx:dxf 51 ed) (cx:dxf 50 ed)))
(if (<= s 0.0) (setq s (+ s (* 2.0 pi))))
(* (cx:dxf 40 ed) s))
((= type "LWPOLYLINE") (cx:lw-length (cx:lw-verts ed) (cx:ent-closed ed)))
((= type "ELLIPSE")
(setq pts (cx:ent-points ed) total 0.0 i 0)
(repeat (1- (length pts))
(setq total (+ total (distance (nth i pts) (nth (1+ i) pts))) i (1+ i)))
total)))
;; Close the undo step and put the system variables back.
(defun cx:end ()
(foreach v cx:sv (setvar (car v) (cdr v)))
(command "_.UNDO" "_End")
(setq cx:sv nil)
(princ))
;; Error handler: restores everything, stays quiet on Esc.
(defun cx:err (msg)
(if (> (getvar "CMDACTIVE") 0) (command))
(cx:end)
(if (and msg (not (wcmatch (strcase msg) "*BREAK*,*CANCEL*,*EXIT*")))
(princ (strcat "\nError: " msg)))
(princ))
;; Real X as text with at most PREC decimals and no trailing zeros ("2.50" -> "2.5", "3.00" -> "3").
(defun cx:num (x prec / s)
(setq s (rtos x 2 prec))
(if (wcmatch s "*.*")
(progn
(while (= (substr s (strlen s)) "0") (setq s (substr s 1 (1- (strlen s)))))
(if (= (substr s (strlen s)) ".") (setq s (substr s 1 (1- (strlen s)))))))
(if (or (= s "-0") (= s "")) "0" s))
(defun cx:ask-real (msg def / v)
(setq v (getreal (strcat "\n" msg " <" (cx:num def 6) ">: ")))
(if v v def))
;; Selection set to a list of entity names.
(defun cx:ss->list (ss / i out)
(setq i 0)
(if ss
(repeat (sslength ss)
(setq out (cons (ssname ss i) out)
i (1+ i))))
(reverse out))
;; Replace every OLD in STR by NEW (case sensitive).
(defun cx:replace (str old new / n out pos)
(setq n (strlen old) out "" pos 1)
(if (= n 0)
str
(progn
(while (<= pos (1+ (- (strlen str) n)))
(if (= (substr str pos n) old)
(setq out (strcat out new) pos (+ pos n))
(setq out (strcat out (substr str pos 1)) pos (1+ pos))))
(strcat out (substr str pos)))))
;; Ask for one of the keywords in BITS ("Yes No"), default DEF; returns the keyword.
(defun cx:ask-kw (msg bits def / v)
(initget 0 bits)
(setq v (getkword (strcat "\n" msg " [" (cx:replace bits " " "/") "] <" def ">: ")))
(if v v def))
;; Save the system variables in VARS ((name . value) ...), set them, and open one undo step.
;; The caller declares cx:sv as a local so the saved values live exactly as long as the command.
(defun cx:start (vars)
(setq cx:sv (mapcar (function (lambda (v) (cons (car v) (getvar (car v))))) vars))
(command "_.UNDO" "_Begin")
(foreach v vars (setvar (car v) (cdr v)))
(princ))
;; A selection set holding the entity names in LST.
(defun cx:list->ss (lst / ss)
(setq ss (ssadd))
(foreach e lst (ssadd e ss))
ss)
(princ "\nCXZEROLEN loaded. Type CXZEROLEN to start.")
(princ)
Version history
Version 1.0.0 · October 27, 2026
First release.
More commands: Clean-up
CXBYLAYERSet color, linetype and lineweight back to ByLayerCXEMPTYTXTFind and delete empty textCXFONTCHKList text styles whose font file is missingCXFONTSUBReplace one font file by another in all text stylesCXPURGEPurge the drawing until nothing more goesCXDELTYPEDelete every object of the type you pickCXDIMRESETReset typed-over dimension text to the measured valueCXDUPDELDelete exact duplicate objectsCXOBJCOUNTCount the objects by type