BIM

Lisp xuất tọa độ trong CAD: tệp chạy được và chỗ bản cũ hỏng

Lisp xuất tọa độ trong CAD ghi nhãn điểm và lập bảng thống kê. Bài có mã đầy đủ để tải, kèm ba chỗ bản đang lưu hành làm hỏng bảng và mất số lẻ.

  • TS. Đỗ Quốc Hoàng
  • 13 phút đọc
Một thửa đất năm đỉnh sau khi chạy lệnh XTD: mỗi góc có nhãn hai dòng toạ độ, và bảng thống kê bên phải Ảnh: Ảnh chụp trên AutoCAD 2027 tại Viện

Người làm hồ sơ đất đai và hạ tầng đi tìm lisp xuất tọa độ trong CAD vì cùng một việc: ghi toạ độ từng góc thửa lên bản vẽ rồi lập bảng thống kê, thay vì gõ tay từng con số.

Viện tải bản đang lưu hành về đọc từng dòng, tìm ra ba chỗ hỏng, rồi viết lại một bản của mình. Bài này có cả mã nguồn lẫn tệp để tải. Muốn học trọn khâu dựng hình và trình bày hồ sơ thì xem khóa AutoCAD Xây Dựng.

Xuất tọa độ trong CAD: việc cần làm gồm những gì

Một nhãn toạ độ đúng quy cách gồm bốn phần: đường dẫn từ điểm ra chỗ trống, hai dòng số xếp chồng, một vòng tròn mang số hiệu điểm, và cuối cùng là bảng thống kê gom hết lại.

Chỗ quyết định nằm ở thứ tự hai con số. Trắc địa Việt Nam quy ước X là trục bắc, Y là trục đông, trong khi AutoCAD thì X là ngang và Y là dọc. Nói cách khác, X trong bảng toạ độ phải lấy từ Y của AutoCAD.

Mọi bản lisp lưu hành đều đổi trục sẵn, và không hỏi bạn một câu nào. Dùng cho bản đồ địa chính thì đúng, nhưng dùng cho mặt bằng kiến trúc thì con số ra ngược mà không có dấu hiệu gì báo.

Một cách gọi khác hay gặp là lisp pick tọa độ trong cad, và nó chỉ ý định hẹp hơn: bấm một điểm rồi lấy ra cặp số, không cần nhãn cũng không cần bảng. Việc ấy AutoCAD làm sẵn bằng lệnh ID, và cách nhập toạ độ bằng bàn phím nằm ở bài nhập tọa độ trong CAD. Tệp lisp chỉ đáng dùng khi bạn cần nhãn và bảng, tức là khi làm hồ sơ chứ không phải khi tra một con số.

Tải lisp xuất tọa độ trong CAD của Viện

Tải tệp XTD-IIC.lsp — 7 KB, hai lệnh. Mã để nguyên trong bài, vì chạy mã lạ trên máy mình là việc đáng cẩn thận.

LệnhViệc nó làm
XTDchọn các điểm, ghi nhãn toạ độ lên bản vẽ và lập bảng thống kê
XTDCSVchọn các điểm rồi ghi thẳng ra tệp .csv, mở bằng Excel

Cả hai đều hỏi quy ước trục trước khi làm, mặc định là trắc địa. Ai vẽ kiến trúc thì gõ C để giữ nguyên trục của AutoCAD.

(vl-load-com)

(defun IIC-GIOI-THIEU ()
  (princ "\nLisp được lập trình bởi RDSIC - Viện Tin học Xây dựng")
  (princ "\nwebsite: https://iic.huce.edu.vn  -  Tel: 0989 427 809")
)

;; Tao chu bang entmake, KHONG dung (command "text" ...): kieu chu co chieu cao
;; co dinh thi dong hoi chieu cao bien mat va moi doi so sau do lech mot nhip.
;; can: 0 trai, 1 giua
(defun IIC:CHU (p cao noi can)
  (entmake (list (cons 0 "TEXT") (cons 10 p) (cons 11 p) (cons 40 cao)
                 (cons 1 noi) (cons 72 can) (cons 73 0)))
)

(defun IIC:NET (a b)
  (entmake (list (cons 0 "LINE") (cons 10 a) (cons 11 b)))
)

(defun IIC:TRON (p r)
  (entmake (list (cons 0 "CIRCLE") (cons 10 p) (cons 40 r)))
)

;; Tra ve (chuoi-X chuoi-Y) theo quy uoc dang chon.
(defun IIC:CAP (p td)
  (if td
    (list (rtos (cadr p) 2 2) (rtos (car p) 2 2))
    (list (rtos (car p) 2 2) (rtos (cadr p) 2 2))
  )
)

(defun IIC:HOI-TRUC ( / k )
  (initget "TracDia Cad")
  (setq k (getkword "\nQuy uoc truc [TracDia/Cad] <TracDia>: "))
  (/= k "Cad")
)

(defun IIC:CHON-DIEM ( / p ds )
  (setq ds nil)
  (while (setq p (getpoint "\nChon diem (Enter de dung): "))
    (setq ds (cons p ds))
  )
  (reverse ds)
)

(defun c:XTD ( / e-cu os-cu ce-cu h td ds n i p cap sx sy
                 p1 p2 p3 rong r pb hang cot1 cot2 cot3 y0 td1 td2 dz-cu )
  (IIC-GIOI-THIEU)
  (setq e-cu *error*
        os-cu (getvar "osmode")
        ce-cu (getvar "cmdecho"))
  ;; Khoi phuc du nguoi dung bam ESC giua chung. Thieu doan nay thi bat diem bi
  ;; tat am tham va nhom hoan tac bi bo ngo - dung loi cua cac ban dang luu hanh.
  (defun *error* (m)
    (setvar "osmode" os-cu) (setvar "cmdecho" ce-cu)
    (if dz-cu (setvar "dimzin" dz-cu))
    (command "_.UNDO" "_End")
    (setq *error* e-cu)
    (if (and m (not (wcmatch (strcase m) "*CANCEL*,*QUIT*")))
      (princ (strcat "\nIIC loi: " m)))
    (princ)
  )
  (command "_.UNDO" "_Begin")
  (setvar "cmdecho" 0)
  ;; DIMZIN = 8 lam rtos cat het so 0 thua, toa do 3200.00 ra thanh 3200.
  ;; Tat trong luc chay roi tra lai, ke ca khi bam ESC.
  (setq dz-cu (getvar "dimzin"))
  (setvar "dimzin" 0)
  (setq h (getvar "TEXTSIZE"))
  (if (<= h 0) (setq h 2.5))
  (setq h (cond ((getreal (strcat "\nChieu cao chu <" (rtos h 2 2) ">: "))) (h)))
  (setq td (IIC:HOI-TRUC))
  (setq ds (IIC:CHON-DIEM))
  (if (null ds)
    (princ "\nKhong chon diem nao.")
    (progn
      (setvar "osmode" 0)
      (setq n (length ds) i 0 rong 0.0 r (* 1.5 h))
      (while (< i n)
        (setq p   (nth i ds)
              cap (IIC:CAP p td)
              sx  (car cap)
              sy  (cadr cap)
              p1  (list (+ (car p) (* 3 h)) (+ (cadr p) (* 3 h)))
              rong (max rong (* (max (strlen sx) (strlen sy)) h 0.72))
        )
        ;; Khe ho chu - net: 0.55 lan chieu cao chu o tren, va o duoi cung vay.
        ;; De 0.35 thi chu dinh sat net, nhin roi (anh Binh bat 20/09/2026).
        (setq p2 (list (+ (car p1) rong (* 0.6 h)) (cadr p1))
              p3 (list (+ (car p2) r (* 0.3 h)) (cadr p1)))
        (IIC:NET p p1)
        (IIC:NET p1 p2)
        (IIC:CHU (list (+ (car p1) (* 0.3 h)) (+ (cadr p1) (* 0.55 h))) h sx 0)
        (IIC:CHU (list (+ (car p1) (* 0.3 h)) (- (cadr p1) (* 1.55 h))) h sy 0)
        (IIC:TRON p3 r)
        (IIC:CHU (list (car p3) (- (cadr p3) (* 0.5 h))) h (strcat "N" (itoa (1+ i))) 1)
        (setq i (1+ i))
      )
      (setq pb (getpoint "\nVi tri dat bang (goc tren ben trai): "))
      (if pb
        (progn
          (setq td1 (if td "Toa do X (bac)" "Toa do X")
                td2 (if td "Toa do Y (dong)" "Toa do Y")
                cot1 (* 5 h)
                cot2 (max (+ rong (* 2 h)) (* (strlen td1) h 0.78))
                cot3 (max (+ rong (* 2 h)) (* (strlen td2) h 0.78))
                hang (* 2.2 h) y0 (cadr pb) i 0)
          (IIC:NET pb (list (+ (car pb) cot1 cot2 cot3) y0))
          (setq y0 (- y0 hang))
          (IIC:CHU (list (+ (car pb) (* 0.5 cot1)) (+ y0 (* 0.6 h))) h "STT" 1)
          (IIC:CHU (list (+ (car pb) cot1 (* 0.5 cot2)) (+ y0 (* 0.7 h))) h td1 1)
          (IIC:CHU (list (+ (car pb) cot1 cot2 (* 0.5 cot3)) (+ y0 (* 0.7 h))) h td2 1)
          (IIC:NET (list (car pb) y0) (list (+ (car pb) cot1 cot2 cot3) y0))
          (while (< i n)
            (setq cap (IIC:CAP (nth i ds) td))
            (IIC:CHU (list (+ (car pb) (* 0.5 cot1)) (- y0 (* 1.4 h))) h
                     (strcat "N" (itoa (1+ i))) 1)
            (IIC:CHU (list (+ (car pb) cot1 (* 0.5 cot2)) (- y0 (* 1.5 h))) h (car cap) 1)
            (IIC:CHU (list (+ (car pb) cot1 cot2 (* 0.5 cot3)) (- y0 (* 1.5 h))) h (cadr cap) 1)
            (setq y0 (- y0 hang) i (1+ i))
            (IIC:NET (list (car pb) y0) (list (+ (car pb) cot1 cot2 cot3) y0))
          )
          (IIC:NET pb (list (car pb) y0))
          (IIC:NET (list (+ (car pb) cot1) (cadr pb)) (list (+ (car pb) cot1) y0))
          (IIC:NET (list (+ (car pb) cot1 cot2) (cadr pb)) (list (+ (car pb) cot1 cot2) y0))
          (IIC:NET (list (+ (car pb) cot1 cot2 cot3) (cadr pb))
                   (list (+ (car pb) cot1 cot2 cot3) y0))
        )
      )
      (princ (strcat "\nDa ghi " (itoa n) " diem."))
    )
  )
  (setvar "osmode" os-cu) (setvar "cmdecho" ce-cu)
  (setvar "dimzin" dz-cu)
  (command "_.UNDO" "_End")
  (setq *error* e-cu)
  (princ)
)

;; XTDCSV - ghi thang ra tep .csv, mo bang Excel duoc ngay.
(defun c:XTDCSV ( / e-cu td ds n i cap f duong dz-cu )
  (IIC-GIOI-THIEU)
  (setq e-cu *error* f nil)
  (setq dz-cu (getvar "dimzin"))
  (setvar "dimzin" 0)
  (defun *error* (m)
    (if f (close f))
    (if dz-cu (setvar "dimzin" dz-cu))
    (setq *error* e-cu)
    (if (and m (not (wcmatch (strcase m) "*CANCEL*,*QUIT*")))
      (princ (strcat "\nIIC loi: " m)))
    (princ)
  )
  (setq td (IIC:HOI-TRUC))
  (setq ds (IIC:CHON-DIEM))
  (if (null ds)
    (princ "\nKhong chon diem nao.")
    (progn
      (setq duong (getfiled "Ghi toa do ra tep" "toado" "csv" 1))
      (if duong
        (progn
          (setq f (open duong "w") n (length ds) i 0)
          (write-line (if td "STT,X (bac),Y (dong)" "STT,X,Y") f)
          (while (< i n)
            (setq cap (IIC:CAP (nth i ds) td))
            (write-line (strcat "N" (itoa (1+ i)) "," (car cap) "," (cadr cap)) f)
            (setq i (1+ i))
          )
          (close f)
          (setq f nil)
          (princ (strcat "\nDa ghi " (itoa n) " diem ra " duong))
        )
      )
    )
  )
  (setvar "dimzin" dz-cu)
  (setq *error* e-cu)
  (princ)
)

Hướng dẫn dùng, từng bước một

Bước 1 — Nạp tệp

APPLOAD, trỏ tới tệp, bấm Load, rồi bấm Close. Muốn khỏi nạp lại mỗi lần mở máy thì thêm tệp vào ô Startup Suite ngay trong hộp ấy. Cách nạp chi tiết nằm ở bài lisp cắt dim.

Bước 2 — Chạy lệnh XTD

XTD rồi Enter. Ba câu hỏi lần lượt hiện ra:

Chieu cao chu <2.50>:

Quy uoc truc [TracDia/Cad] <TracDia>:

Chon diem (Enter de dung):

Chiều cao chữ để mặc định là lấy theo biến TEXTSIZE của bản vẽ. Quy ước trục cứ Enter nếu làm hồ sơ đất đai. Rồi bấm lần lượt các góc thửa, xong thì Enter.

Bước 3 — Đặt bảng thống kê

Vi tri dat bang (goc tren ben trai):

Bấm một điểm ở chỗ trống. Bảng tự dựng với ba cột, bề rộng cột lấy theo con số dài nhất, và tiêu đề ghi rõ cột nào là bắc, cột nào là đông. Máy báo:

Da ghi 5 diem.

Bước 4 — Hoặc xuất thẳng ra Excel

XTDCSV, chọn điểm như trên, rồi chọn chỗ lưu. Tệp .csv mở bằng Excel ra đúng ba cột, khỏi phải chép tay từ bảng trên bản vẽ sang bảng tính.

Ba chỗ bản lisp đang lưu hành làm hỏng

Chúng tôi tải bản phổ biến nhất về, giải nén, và đọc hết 104 dòng mã. Nó chạy được, và ý tưởng thì đúng. Nhưng có ba chỗ hỏng, cả ba đều kiểm được trên máy.

Bảng thống kê của bản đang lưu hành Ảnh: Tiêu đề bảng do bản lisp lưu hành sinh ra: hai chữ Täa ®é chính là Tọa độ viết bằng bảng mã TCVN3 đời cũ. Cột số cũng mất hẳn phần thập phân

Thứ nhất là bảng mã. Trong mã nguồn, tiêu đề cột viết là Täa ®é Y. Đó là chữ Tọa độ Y gõ bằng bảng mã TCVN3 của những năm 2000, chỉ hiện đúng khi bản vẽ dùng đúng phông ấy. Trên máy dùng phông Unicode thông thường, nó ra đúng như ảnh trên.

Thứ hai là phần thập phân. Trang rtos của Autodesk ghi rõ hàm này chịu ảnh hưởng của nhiều biến: "according to the settings of mode, precision, and the AutoCAD UNITMODE, DIMZIN, LUNITS, and LUPREC system variables". Hàm rtos cắt sạch số 0 thừa khi biến DIMZIN bằng 8, mà 8 là giá trị của phần lớn bản vẽ theo hệ mét. Toạ độ 584320.00 in ra thành 584320. Hồ sơ trắc địa mất hai chữ số thập phân là hỏng. Bản của Viện tắt DIMZIN trong lúc chạy rồi trả lại nguyên giá trị cũ, kể cả khi bạn bấm ESC.

Thứ ba là không có bẫy lỗi. Bản cũ mở một nhóm hoàn tác bằng Undo Begin nhưng không có hàm *error*. Bấm ESC giữa chừng là nhóm ấy bỏ ngỏ, và biến CMDECHO nằm lại ở 0 — chúng tôi đo được đúng con số ấy sau khi bấm ESC. Dòng lệnh từ đó thôi hiện lệnh đang chạy, và người dùng không hiểu vì sao.

Nói cho công bằng: có một điều chúng tôi đoán sai. Chúng tôi tưởng bản cũ cũng bỏ quên chế độ bắt điểm, nhưng đo thì OSMODE vẫn về đúng giá trị cũ. Chỗ ấy họ làm đúng.

Mã của Viện khác ở đâu

ChỗBản lưu hànhBản của Viện
Quy ước trụcđổi sẵn, không hỏihỏi một câu, mặc định trắc địa
Phần thập phânphụ thuộc DIMZIN, hay mấtép đúng hai chữ số rồi trả biến về
Bấm ESC giữa chừngnhóm hoàn tác bỏ ngỏ, CMDECHO kẹt ở 0có hàm *error*, trả lại hết
Tiêu đề bảngbảng mã TCVN3, ra chữ lạchữ thường, không dấu, chạy ở mọi phông
Bề rộng cộttính theo số, tiêu đề tràn sang cột bênlấy số lớn hơn giữa bề rộng số và bề rộng tiêu đề
Xuất ra Excelkhông cólệnh XTDCSV

Một khác biệt nữa nằm ở chỗ ít ai để ý: biến cục bộ. Bản cũ để bảy biến chạy ra ngoài phạm vi hàm, gồm cả những tên rất dễ trùng như kn. Tệp lisp khác đang nạp cùng phiên mà dùng chung tên ấy thì hai bên ghi đè lẫn nhau, và lỗi hiện ra ở chỗ chẳng liên quan gì. Bản của Viện khai hết biến trong danh sách cục bộ.

Còn một chi tiết kỹ thuật đáng nói. Bản cũ tạo chữ bằng (command "text" ...). Cách ấy hỏng khi kiểu chữ hiện hành đặt chiều cao cố định, vì lúc đó dòng hỏi chiều cao biến mất và mọi đối số sau đó lệch đi một nhịp. Bản của Viện dựng chữ bằng entmake, không phụ thuộc kiểu chữ.

Ba điều hay nghe nhưng không đúng

Điều hay ngheThực tế
Lisp xuất toạ độ nào cũng như nhauKhác ở chỗ đổi trục, số lẻ và bẫy lỗi — ba chỗ quyết định hồ sơ đúng hay sai
Bảng ra chữ lạ là do máy thiếu phôngLà do mã nguồn viết bằng bảng mã đời cũ, đổi phông không chữa được
Muốn có tệp Excel thì phải chép tayGhi thẳng ra .csv từ lisp, mở bằng Excel là xong

Câu hỏi thường gặp

Lisp lấy tọa độ chạy xong mà số không có phần thập phân?

Đó là biến DIMZIN. Đặt nó về 0 trước khi chạy, hoặc dùng tệp ở bài này vì nó tự xử lý rồi trả biến về.

Vì sao cột X trong bảng lại là số của trục đứng?

Vì quy ước trắc địa Việt Nam lấy X là trục bắc. AutoCAD gọi trục đứng là Y, nên bảng phải đổi. Làm mặt bằng kiến trúc thì chọn Cad để giữ nguyên.

Ghi tọa độ trong CAD cho hàng trăm điểm có chậm không?

Không. Lệnh chỉ vẽ nét và chữ, không gọi lệnh nặng nào. Chỗ mất thời gian là bấm chuột chọn điểm.

Bấm nhầm rồi thì sao?

U một lần. Cả lượt được gộp thành một bước hoàn tác, nên không phải bấm nhiều lần.

Tệp .csv mở bằng Excel bị dồn hết vào một cột?

Đó là do Excel đang đặt dấu phân cách là chấm phẩy. Mở bằng chức năng nhập dữ liệu rồi chọn dấu phẩy, hoặc đổi trong tuỳ chọn vùng của Windows.

Về tác giả

TS. Đỗ Quốc Hoàng — Phó Viện trưởng Viện Tin học Xây dựng – Trường Đại học Xây dựng Hà Nội, chuyên gia lập trình phần mềm trong xây dựng