CADXChange

Todos los comandos LISP

CXAREAMedición y polilíneasVersión 1.0.0Probado en AutoCAD

Escribir el área dentro de figuras cerradas

Escribe el área como texto en el centro de cada polilínea cerrada, círculo o elipse, en la unidad que quieras, con tu propio prefijo y sufijo.

Cómo se usa

  1. Escribe CXAREA y selecciona las figuras.
  2. Introduce un factor de unidad: 0.000001 convierte mm² en m², 1 mantiene las unidades del dibujo.
  3. Introduce los decimales y, si quieres, un texto antes y después del número.

Conviene saber

  • El texto se coloca donde cabe dentro, incluso en contornos en L o en U. Las polilíneas abiertas se omiten.

Cargarlo en AutoCAD

  1. Guarda el archivo donde quieras.
  2. Escribe APPLOAD, elige el archivo y pulsa Cargar.
  3. Escribe el nombre del comando: CXAREA.

Para tenerlo en cada inicio, agrégalo al Contenido de inicio en el mismo cuadro de diálogo, o carga una carpeta entera de una vez con el cargador opcional. Sobre el cargador opcional

Probado en AutoCAD (versión completa). Otros programas que ejecutan AutoLISP pueden funcionar, pero no se han probado.

El código

Texto sin más, nada oculto. Cópialo en un archivo llamado cxarea.lsp o descárgalo.

;;; ============================================================================
;;; CXAREA - Write the area inside closed shapes
;;; Version 1.0.0
;;; CADXChange LISP tools - https://cadxchange.net/resources/lisp/cxarea
;;; 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 area of every closed polyline, circle and full ellipse in NAMES into the drawing, as a text centered
;; inside the shape. The area is multiplied by FACTOR (a unit conversion), shown with PREC decimals between
;; PREFIX and SUFFIX, and the text gets height HEIGHT and layer LAYER. Other objects are skipped.
;; Returns (count total-area) with the total in drawing units.
(defun cx:area-run (names factor prec prefix suffix height layer / n total ed area)
  (setq n 0 total 0.0)
  (foreach e names
    (setq ed   (entget e)
          area (cx:ent-area ed))
    (if area
      (progn
        (cx:label (cx:ent-label-point ed) (strcat prefix (cx:num (* area factor) prec) suffix) height 0.0 layer)
        (setq total (+ total area) n (1+ n)))))
  (list n total))

(defun c:CXAREA ( / *error* cx:sv ss factor prec prefix suffix r)
  (setq *error* cx:err)
  (if (setq ss (cx:pick "\nSelect closed polylines, circles or ellipses: " '((0 . "LWPOLYLINE,CIRCLE,ELLIPSE"))))
    (progn
      (setq factor (cx:ask-real "Multiply the area by (unit conversion, 0.000001 turns mm2 into m2)" 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:area-run (cx:ss->list ss) factor prec prefix suffix (getvar "TEXTSIZE") (getvar "CLAYER")))
      (princ (strcat "\n" (itoa (car r)) " area(s) written. Total: " (cx:num (* (cadr r) factor) prec)))
      (cx:end)))
  (princ))


;;; ---- Shared helpers (used by the command above; identical in every CADXChange tool) ----

(defun cx:dxf (code ed)
  (cdr (assoc code ed)))

;; 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 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 area of a closed polyline with its arc segments.
(defun cx:lw-area (verts / sum i n a v1 v2)
  (setq sum 0.0 i 0 n (length verts))
  (repeat n
    (setq v1  (nth i verts)
          v2  (nth (rem (1+ i) n) verts)
          sum (+ sum (/ (- (* (car v1) (cadr v2)) (* (car v2) (cadr v1))) 2.0))
          a   (cx:bulge-arc v1 v2 (nth 2 v1)))
    (if a
      (setq sum (+ sum (* 0.5 (nth 2 a) (nth 2 a) (- (nth 4 a) (sin (nth 4 a)))))))
    (setq i (1+ i)))
  (abs sum))

;; The area of a closed polyline, a circle or a full ellipse; nil for other objects.
(defun cx:ent-area (ed / type a)
  (setq type (cx:dxf 0 ed))
  (cond
    ((not (cx:ent-closed ed)) nil)
    ((= type "LWPOLYLINE") (cx:lw-area (cx:lw-verts ed)))
    ((= type "CIRCLE") (* pi (cx:dxf 40 ed) (cx:dxf 40 ed)))
    ((= type "ELLIPSE")
     (setq a (distance '(0.0 0.0 0.0) (cx:dxf 11 ed)))
     (* pi a a (cx:dxf 40 ed)))))

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

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

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

;; The centroid of the polygon PTS ((x y) ...) and its area: (centroid area).
(defun cx:poly-centroid (pts / a sx sy i n p q f)
  (setq a 0.0 sx 0.0 sy 0.0 i 0 n (length pts))
  (repeat n
    (setq p  (nth i pts)
          q  (nth (rem (1+ i) n) pts)
          f  (- (* (car p) (cadr q)) (* (car q) (cadr p)))
          a  (+ a f)
          sx (+ sx (* (+ (car p) (car q)) f))
          sy (+ sy (* (+ (cadr p) (cadr q)) f)))
    (setq i (1+ i)))
  (if (< (abs a) 1e-12)
    (list (list (car (car pts)) (cadr (car pts))) 0.0)
    (list (list (/ sx (* 3.0 a)) (/ sy (* 3.0 a))) (abs (/ a 2.0)))))

;; True when the point PT lies inside the polygon PTS (ray casting).
(defun cx:pt-in-poly (pt pts / inside i n p q)
  (setq inside nil i 0 n (length pts))
  (repeat n
    (setq p (nth i pts) q (nth (rem (1+ i) n) pts))
    (if (and (/= (> (cadr p) (cadr pt)) (> (cadr q) (cadr pt)))
             (< (car pt) (+ (car p) (/ (* (- (cadr pt) (cadr p)) (- (car q) (car p))) (- (cadr q) (cadr p))))))
      (setq inside (not inside)))
    (setq i (1+ i)))
  inside)

;; Merge sort of LST with PRED (a function of two items that is true when the first goes first).
(defun cx:sort (lst pred / half a b out)
  (cond
    ((or (null lst) (null (cdr lst))) lst)
    (T
     (setq half (/ (length lst) 2) a nil b lst)
     (repeat half (setq a (cons (car b) a) b (cdr b)))
     (setq a (cx:sort (reverse a) pred) b (cx:sort b pred) out nil)
     (while (and a b)
       (if (apply pred (list (car b) (car a)))
         (setq out (cons (car b) out) b (cdr b))
         (setq out (cons (car a) out) a (cdr a))))
     (append (reverse out) a b))))

;; A point inside the polygon PTS: the centroid when it is inside, otherwise the middle of the widest
;; stretch of the horizontal line through the centroid.
(defun cx:interior-point (pts / c y xs i n p q best from to w)
  (setq c (car (cx:poly-centroid pts)))
  (if (cx:pt-in-poly c pts)
    c
    (progn
      (setq y (cadr c) n (length pts) i 0)
      (repeat n
        (setq p (nth i pts) q (nth (rem (1+ i) n) pts))
        (if (/= (> (cadr p) y) (> (cadr q) y))
          (setq xs (cons (+ (car p) (/ (* (- y (cadr p)) (- (car q) (car p))) (- (cadr q) (cadr p)))) xs)))
        (setq i (1+ i)))
      (setq xs (cx:sort xs '<) best 0.0 i 0)
      (while (< (1+ i) (length xs))
        (setq from (nth i xs) to (nth (1+ i) xs) w (- to from))
        (if (> w best) (setq best w c (list (/ (+ from to) 2.0) y)))
        (setq i (+ i 2)))
      c)))

;; A point inside a closed object where its label can go.
(defun cx:ent-label-point (ed)
  (if (= (cx:dxf 0 ed) "CIRCLE")
    (list (car (cx:dxf 10 ed)) (cadr (cx:dxf 10 ed)))
    (cx:interior-point (cx:ent-points ed))))

;; 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 "\nCXAREA loaded. Type CXAREA to start.")
(princ)

Historial de versiones

Versión 1.0.0 · 27 de octubre de 2026

Primera versión.

Más comandos: Medición y polilíneas