CADXChange

모든 LISP 명령

CXLEN측정과 폴리선버전 1.0.0AutoCAD에서 테스트함

선·호·폴리선을 따라 길이 쓰기

각 객체의 길이를 선 방향을 따라 읽기 쉽게 옆에 씁니다. 원에는 둘레를 씁니다.

사용 방법

  1. CXLEN을 입력하고 객체를 선택합니다.
  2. 단위 계수를 입력합니다. 예를 들어 0.001이면 mm 도면을 미터로 표시합니다.
  3. 소수점 자릿수와 앞뒤에 붙일 글자(선택)를 입력합니다.

알아 두세요

  • 폴리선 안의 호 구간은 호의 길이로 잽니다.

AutoCAD에서 불러오기

  1. 파일을 원하는 곳에 저장합니다.
  2. APPLOAD를 입력하고 파일을 선택한 뒤 '로드'를 누릅니다.
  3. 명령 이름을 입력합니다: CXLEN.

시작할 때마다 쓰려면 같은 대화상자에서 '시작 그룹'에 추가하거나, 선택 사항인 로더로 폴더 전체를 한 번에 불러옵니다. 선택 사항인 로더에 대해

AutoCAD(정식 버전)에서 테스트했습니다. AutoLISP를 실행하는 다른 프로그램에서도 될 수 있지만 테스트하지 않았습니다.

코드

숨겨진 부분이 없는 일반 텍스트입니다. cxlen.lsp 이름의 파일에 복사하거나 내려받으세요.

;;; ============================================================================
;;; CXLEN - Write the length along lines, arcs and polylines
;;; Version 1.0.0
;;; CADXChange LISP tools - https://cadxchange.net/resources/lisp/cxlen
;;; 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
;;; ============================================================================

;; Write the length of every line, arc, polyline and ellipse in NAMES next to the object, along it, halfway.
;; A circle gets its circumference, written at its center. The length is multiplied by FACTOR, shown with PREC
;; decimals between PREFIX and SUFFIX, with height HEIGHT on layer LAYER. Returns (count total-length).
(defun cx:len-run (names factor prec prefix suffix height layer / n total ed len mid ang pt)
  (setq n 0 total 0.0)
  (foreach e names
    (setq ed  (entget e)
          len (cx:ent-length ed))
    (if len
      (progn
        (if (= (cx:dxf 0 ed) "CIRCLE")
          (setq pt (cx:dxf 10 ed) ang 0.0)
          (setq mid (cx:path-mid (cx:ent-points ed))
                ang (cx:readable (cadr mid))
                ;; a little above the line, on the side the text reads from
                pt  (list (- (car (car mid)) (* 0.8 height (sin ang))) (+ (cadr (car mid)) (* 0.8 height (cos ang))))))
        (cx:label pt (strcat prefix (cx:num (* len factor) prec) suffix) height ang layer)
        (setq total (+ total len) n (1+ n)))))
  (list n total))

(defun c:CXLEN ( / *error* cx:sv ss factor prec prefix suffix r)
  (setq *error* cx:err)
  (if (setq ss (cx:pick "\nSelect lines, arcs, polylines, circles or ellipses: " '((0 . "LINE,ARC,LWPOLYLINE,CIRCLE,ELLIPSE"))))
    (progn
      (setq factor (cx:ask-real "Multiply the length by (unit conversion, 0.001 turns mm into m)" 1.0)
            prec   (cx:ask-int "Decimals" 2)
            prefix (cx:ask-str "Text in front (- for none)" "")
            suffix (cx:ask-str "Text after the number (- for none)" ""))
      (cx:start '(("CMDECHO" . 0)))
      (setq r (cx:len-run (cx:ss->list ss) factor prec prefix suffix (getvar "TEXTSIZE") (getvar "CLAYER")))
      (princ (strcat "\n" (itoa (car r)) " length(s) written. Total: " (cx:num (* (cadr r) factor) prec)))
      (cx:end)))
  (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)))

;; The point halfway along the polyline PTS ((x y) ...) and the direction of the path there: ((x y) angle).
(defun cx:path-mid (pts / total i n half run seg)
  (setq total 0.0 i 0 n (length pts))
  (repeat (1- n)
    (setq total (+ total (distance (nth i pts) (nth (1+ i) pts))) i (1+ i)))
  (setq half (/ total 2.0) run 0.0 i 0)
  (while (and (< (1+ i) (1- n)) (< (+ run (distance (nth i pts) (nth (1+ i) pts))) half))
    (setq run (+ run (distance (nth i pts) (nth (1+ i) pts))) i (1+ i)))
  (setq seg (distance (nth i pts) (nth (1+ i) pts)))
  (list (if (< seg 1e-12)
          (nth i pts)
          (list (+ (car (nth i pts)) (* (/ (- half run) seg) (- (car (nth (1+ i) pts)) (car (nth i pts)))))
                (+ (cadr (nth i pts)) (* (/ (- half run) seg) (- (cadr (nth (1+ i) pts)) (cadr (nth i pts)))))))
        (angle (nth i pts) (nth (1+ i) pts))))

;;; File and CSV helpers. See helpers.lsp for the format.

;; An angle turned by 180 degrees when needed so that text along it is never upside down.
(defun cx:readable (ang / a)
  (setq a ang)
  (while (< a 0.0) (setq a (+ a (* 2.0 pi))))
  (while (>= a (* 2.0 pi)) (setq a (- a (* 2.0 pi))))
  (if (and (> a (+ (/ pi 2.0) 1e-9)) (<= a (* 1.5 pi)))
    (- a pi)
    (if (> a (* 1.5 pi)) (- a (* 2.0 pi)) a)))

;; A text centered on PT, with height H, rotation ROT (radians) and layer LAYER, in the current text style.
(defun cx:label (pt str h rot layer)
  (entmakex (list '(0 . "TEXT") (cons 8 layer) (list 10 (car pt) (cadr pt) 0.0) (cons 40 h) (cons 1 str)
                  (cons 50 rot) (cons 7 (getvar "TEXTSTYLE")) '(72 . 1)
                  (list 11 (car pt) (cadr pt) 0.0) '(100 . "AcDbText") '(73 . 2))))

;; 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))

;; 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))

;; Use the objects already selected, or ask the user to select; FILTER is an ssget filter list or nil.
(defun cx:pick (msg filter / ss)
  (setq ss (if filter (ssget "_I" filter) (ssget "_I")))
  (if (null ss)
    (progn
      (princ msg)
      (setq ss (if filter (ssget filter) (ssget)))))
  ss)

(defun cx:ask-real (msg def / v)
  (setq v (getreal (strcat "\n" msg " <" (cx:num def 6) ">: ")))
  (if v v def))

;; Ask for an integer with a default; returns the default on Enter.
(defun cx:ask-int (msg def / v)
  (setq v (getint (strcat "\n" msg " <" (itoa def) ">: ")))
  (if v v def))

;; Ask for a string (spaces allowed) with a default; "-" means an empty string.
(defun cx:ask-str (msg def / v)
  (setq v (getstring T (strcat "\n" msg " <" def ">: ")))
  (cond ((= v "") def) ((= v "-") "") (T v)))

;; 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))

;; 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))

(princ "\nCXLEN loaded. Type CXLEN to start.")
(princ)

버전 기록

버전 1.0.0 · 2026년 10월 27일

첫 번째 릴리스.

다른 명령: 측정과 폴리선