Đến nội dung


Hình ảnh
* * * * - 4 Bình chọn

Học AutoLisp


  • Please log in to reply
182 replies to this topic

#1 Nguyen Hoanh

Nguyen Hoanh

    biết lệnh adcenter

  • Moderator
  • PipPipPipPipPipPipPip
  • 4105 Bài viết
Điểm đánh giá: 4495 (đỉnh cao)

Đã gửi 17 January 2007 - 11:29 AM

- Tôi xin thử nghiệm một chuỗi bài viết về đào tạo AutoLisp thông qua các dự án thực tế.
Xin mọi người đóng góp ý kiến.
  • 17

#2 Nguyen Hoanh

Nguyen Hoanh

    biết lệnh adcenter

  • Moderator
  • PipPipPipPipPipPipPip
  • 4105 Bài viết
Điểm đánh giá: 4495 (đỉnh cao)

Đã gửi 17 January 2007 - 01:08 PM

Phần 1: bắt đầu

Đầu tiên, chúng ta hãy bắt đầu bằng chương trình viết chữ.

Hãy tạo một file mới và save nó với tên hoclisp.lsp.
(princ "\nChao cadviet")
(princ)

Đây là đoạn mã đơn giản nhất của AutoLisp nhằm viết ra một dòng 'Chao cadviet' trên màn hình nhập dữ liệu của autocad. để sử dụng, appload file hoclisp.lsp này. ta sẽ thấy trên màn hình xuất hiện chữ Chao cadviet.

Đi sâu vào phân tích lệnh, ta thấy: mỗi hàm của cad được bắt đầu bằng dấu '(' ngay sau đó là tên hàm, tiếp đến là thông số của hàm (có thể có, có thể không có) và kết thúc bằng dấu ')' Trong trường hợp này princ là tên hàm, "\nChao cadviet" là tham số của hàm princ.

Trong hai câu lệnh gọi hàm vừa rồi, câu thứ nhất là để viết chữ ra màn hình text, câu lệnh thứ 2 dùng để ẩn kết thúc mà không hiển thị thêm gì cả. Nếu thiếu câu lệnh thứ 2, bạn sẽ nhìn thấy 'Chao cadviet"\nChao cadviet"' trên màn hình, trong đó "\nChao cadviet" là kết quả của hàm (princ "\nChao cadviet").
  • 29

#3 Nguyen Hoanh

Nguyen Hoanh

    biết lệnh adcenter

  • Moderator
  • PipPipPipPipPipPipPip
  • 4105 Bài viết
Điểm đánh giá: 4495 (đỉnh cao)

Đã gửi 17 January 2007 - 01:21 PM

Phần 2:

Trong phần 1, chúng ta đã xây dựng được 1 chương trình lisp có thể nói là thô sơ nhất thế giới. Và nó có 1 nhược điểm: mỗi lần load thì viết ra lệnh. Muốn viết lại phải appload thêm 1 lần nữa.

Để điều khiển được thời điểm viết, và viết được nhiều lần, chúng ta phải định nghĩa một hàm AutoCAD, để mỗi khi gọi hàm này, chương trình sẽ viết ra màn hình text mà không cần phải appload lại file lisp.

Trên file hoclisp.lsp, chúng ta thêm mã lệnh để trở thành như sau:

(defun c:chao()
(princ "\nChao cadviet")
(princ)
)


Như vậy chúng ta đã định nghĩa được một lệnh của AutoCAD mang tên chao, mỗi lần gọi lệnh chao tại dòng nhắc Command, chương trình sẽ viết ra trên màn hình text dòng chữ: Chao cadviet.

Trong 2 dòng vừa thêm, hàm defun là hàm định nghĩa lệnh AutoCAD. Có cấu trúc:
(defun tenham() noidungham) trong đó:
- tenham là tên hàm cần định nghĩa, nếu muốn định nghĩa một lệnh trong AutoCAD thì thêm 'C:' vào trước tên hàm.
- noidungham là tập các lệnh mà hàm vừa định nghĩa sẽ thực thi.
  • 14

#4 Nguyen Hoanh

Nguyen Hoanh

    biết lệnh adcenter

  • Moderator
  • PipPipPipPipPipPipPip
  • 4105 Bài viết
Điểm đánh giá: 4495 (đỉnh cao)

Đã gửi 17 January 2007 - 01:43 PM

Phần 3:

Trong phần 2, chúng ta đã tạo được một chương trình lisp có giao tiếp với AutoCAD thông qua tên lệnh.
Tại phần 3 này, chúng ta sẽ viết một chương trình thực sự, có ích hẳn hoi.

Đó là chương trình tính diện tích của một đối tượng. Trên file hoclisp.lsp, hãy sửa tên hàm chao thành tdt và thêm các mã lệnh như sau:
(defun c:tdt( / sel ent dientich)
(princ "\nChao cadviet")

(setq sel (entsel "\nHay chon doi tuong: "))
(setq ent (car sel))
(command ".area" "o" ent)
(setq dientich (getvar "area"))
(princ "\nDien tich doi tuong vua roi la: ")
(princ dientich)
(princ "\nm2")

(princ)
)

Bạn thử appload file hoclisp.lsp mới và gọi lệnh tdt thử xem, chương trình sẽ yêu cầu bạn nhập vào 1 đối tượng. Và nó sẽ kết xuất diện tích đối tượng vừa rồi ra màn hình:
Command: tdt
Chao cadviet
Hay chon doi tuong: .area
Specify first corner point or [Object/Add/Subtract]: o
Select objects:
Area = 46546736.0000, Perimeter = 28320.0000

Command:
Dien tich doi tuong vua roi la: 4.65467e+007m2

Điều gì làm nên sự khác biệt này vậy: chúng ta hãy lần theo từng lệnh nhé.
- Lệnh (entsel "\nHay chon doi tuong: ") là lệnh trả về đối tượng được pick, kết quả trả về là một tập hợp gồm 2 thành phần: thứ nhất là đối tượng được pick, thứ 2 là tọa độ pick.
- Lệnh (setq sel (entsel "\nHay chon doi tuong: ")) là lệnh gán kết quả vừa pick vào biến sel. Bạn nhớ là lệnh (setq a :lol: là gán giá trị a bằng giá trị b. Trong trường hợp này a là sel, b là kết quả của hàm entsel.
- Lệnh (car sel) là lệnh lấy giá trị đầu tiên của biến sel (biến sel có dạng tập hợp) tức là đối tượng được chọn.
- lệnh (setq ent (car sel)) là lệnh gán giá trị biến ent bằng ename của đối tượng được chọn. ename của một đối tượng là giá trị chỉ ra đối tượng trong một bản vẽ cad.
Như vậy, 2 dòng lệnh
(setq sel (entsel "\nHay chon doi tuong: "))
(setq ent (car sel))
có ý nghĩa là gán giá trị biến ent cho đối tượng vừa được chọn bằng phương pháp pick.

Tiếp đến, lệnh (command ".area" "o" ent) tương đương với gõ tại dòng lệnh command: .area rồi gõ tiếp o và pick đối tượng. Bạn nhớ là lệnh (command a b c d e f...) tương đương với việc gõ các giá trị a, b, c, d, e, f ... vào dòng lệnh của cad. Như vậy, lệnh trên tương đương với việc tính diện tích của đối tượng vừa được chọn.

- Lệnh (setq dientich (getvar "area")): gán giá trị biến dientich bằng giá trị của biến hệ thống AREA (biến hệ thống AREA chứa diện tích vừa được tính bằng lệnh AREA trước đó của cad).

-cụm lệnh:
(princ "\nDien tich doi tuong vua roi la: ")
(princ dientich)
(princ "m2")
chắc chắn bạn biết đó là lệnh viết dòng chữ 'Dien tich doi tuong vua roi la: ' diện tích đối tượng vừa rồi 'm2'.
Trên màn hình bạn còn nhìn thấy các dòng chữ khác ngoài dòng chữ trên đó là do các lệnh area của CAD sinh ra. Trong bài học sau chúng ta sẽ biết cách loại bỏ chữ này.
  • 29

#5 Hieuss

Hieuss

    biết lệnh mtext

  • Vip
  • PipPipPipPip
  • 286 Bài viết
Điểm đánh giá: 163 (tàm tạm)

Đã gửi 22 January 2007 - 10:50 PM

Giáo trình hay lắm, Hoành viết tiếp đi nhé
  • 1

#6 bemove

bemove

    biết lệnh divide

  • Vip
  • PipPipPipPipPipPip
  • 446 Bài viết
Điểm đánh giá: 1308 (rất tốt)

Đã gửi 22 March 2007 - 08:17 PM

Anh Hoành ơi viết tiếp phần cơ bản đi để anh em có cái móng thì mới tự học được. Chương trình nào cũng thế, phải cần 1 người chỉ đường thì mới nhanh được
  • 11

#7 patpat

patpat

    biết pan

  • Members
  • Pip
  • 5 Bài viết
Điểm đánh giá: 1 (bình thường)

Đã gửi 25 March 2007 - 10:43 AM

Sao lâu quá vậy ông anh cạn vốn rồi à .Anh em nào hiểu biết về cái này chỉ bảo cho anh em chút đi
  • 1

#8 Nguyen Hoanh

Nguyen Hoanh

    biết lệnh adcenter

  • Moderator
  • PipPipPipPipPipPipPip
  • 4105 Bài viết
Điểm đánh giá: 4495 (đỉnh cao)

Đã gửi 25 March 2007 - 10:58 PM

Patpat lại khích tướng rồi. Xin giới thiệu phần tiếp theo để mọi người khỏi phải chờ, hy vọng là nó có ích.

Phần 4:
* Các phần trước, chúng ta đã thực hành được với lệnh lisp tính diện tích của một đối tượng. Phần này, chúng ta sẽ đi vào cải tiến để lệnh tính diện tích sẽ áp dụng cho một tập đối tượng được chọn của chúng ta.

* Trong AutoLisp tập đối tượng được lưu trong biến kiểu tập chọn. Với lệnh (ssget), có thể tạo ra một tập chọn chứa các đối tượng chúng ta chọn. Với lệnh (ssname ss index) chúng ta sẽ lấy được entname đối tượng thứ index trong tập đối tượng ss. lệnh (sslength ss) trả về số đối tượng trong tập chọn.

* Tính diện tích của một tập đối tượng, ta sẽ tính diện tích của từng đối tượng trong tập này và cộng lại với nhau. Để làm được như vậy, chúng ta sẽ làm một hàm lặp như sau:
(repeat sodoituong
(setq
ent_ht (ssname ssdt index)
index (+ index 1)
dientich (tinhdientich_one ent_ht)
tongdientich (+ tongdientich dientich)
)
Vòng lặp này sẽ quét qua tất cả các đối tượng và cộng dồn diện tích của đối tượng hiện tại vào diện tích tổng. Trong đoạn mã trên, biến sodoituong chứa số đối tượng có trong tập chọn. index là biến chứa chỉ số của đối tượng hiện tại (index có giá trị từ 0 đến sodoituong-1). ent_ht là tên của đối tượng hiện tại. tinhdientich_one là hàm tính diện tích của một đối tượng được chỉ định, hàm này có được bằng cách gom 2 dòng lệnh của phần trước lại với nhau là: (command ".area" "o" ent)(setq dientich (getvar "area")). Tongdientich chính là diện tích của tất cả các đối tượng, giá trị này có được nhờ cộng dồn tất cả các giá trị của biến dientich lại với nhau.

* Chương trình tính diện tích sẽ tiếp tục được cải tiến như sau:
--------------------Gốc (xin nhắc lại phần trước):
(defun c:tdt( / sel ent dientich)
(princ "\nChao cadviet")
(setq sel (entsel "\nHay chon doi tuong: "))
(setq ent (car sel))
(command ".area" "o" ent)
(setq dientich (getvar "area"))
(princ "\nDien tich tap doi tuong vua roi la: ")
(princ dientich)
(princ "\nm2")
(princ)
)

--------------------Cải tiến (màu xám là đoạn code bỏ đi, màu xanh là đoạn code thêm mới):
(defun c:tdt( / sel ent dientich)
(princ "\nChao cadviet")
;(setq sel (entsel "\nHay chon doi tuong: "))
(setq ssdt (ssget))
;(setq ent (car sel))
(defun tinhdientich_one(ent)
(command ".area" "o" ent)
(setq dientich (getvar "area"))
)

; khởi tạo các biến
(setq
sodoituong (sslength ssdt)
index 0
tongdientich 0.0
)

; vòng lặp quét qua các đối tượng
(repeat sodoituong
(setq
ent_ht (ssname ssdt index)
index (1+ index)
dientich (tinhdientich_one ent_ht)
tongdientich (+ tongdientich dientich)
)
)

(princ "\nDien tich tap doi tuong vua roi la: ")
;(princ dientich)

;in tổng diện tích
(princ tongdientich)

(princ "\nm2")
(princ)
)

* Như vậy, chúng ta đã tạo được một đoạn code tính diện tích của một tập đối tượng. Tuy nhiên, đoạn code này hơi rườm rà, do có một lệnh định nghĩa hàm defun tinhdientich_one nằm ngay giữa đoạn code. Bây giờ, chúng ta sẽ làm sạch lại đoạn code trên bằng cách sắp xếp lại mà không thay đổi bất cứ ý nghĩa của một mã nào:
(defun c:tdt( / sel ent dientich)


;định nghĩa hàm tính diện tích 1 đối tượng
(defun tinhdientich_one(ent)
(command ".area" "o" ent)
(setq dientich (getvar "area"))
)


; viết lời chào và chọn đối tượng

(princ "\nChao cadviet")
(setq ssdt (ssget))


; khởi tạo các biến

(setq
sodoituong (sslength ssdt)
index 0
tongdientich 0.0
)


; quét qua tất cả các đối tượng trong tập chọn, tính diện tích và cộng dồn diện tích

(repeat sodoituong
(setq
ent_ht (ssname ssdt index)
index (1+ index)
dientich (tinhdientich_one ent_ht)
tongdientich (+ tongdientich dientich)
)
)


; xuất kết quả ra màn hình

(princ "\nDien tich tap doi tuong vua roi la: ")
(princ tongdientich)
(princ "\nm2")
(princ)
)


--------------------------------------------------------------------------------------------
Phần tiếp theo sẽ trình bày phần tinh chỉnh code để:
- kết xuất kết quả ra đối tượng text
- lọc các đối tượng được chọn
- ẩn các đoạn text trung gian.

Cảm ơn các bạn vẫn đang theo dõi.
  • 21

#9 bemove

bemove

    biết lệnh divide

  • Vip
  • PipPipPipPipPipPip
  • 446 Bài viết
Điểm đánh giá: 1308 (rất tốt)

Đã gửi 26 March 2007 - 12:05 AM

Quá hay!!! :lol: Come on! kiểu này dễ hiểu hơn đọc ebook rồi. anh Hoành move topic này vào phần tutorial hợp lý hơn
  • 0

#10 bemove

bemove

    biết lệnh divide

  • Vip
  • PipPipPipPipPipPip
  • 446 Bài viết
Điểm đánh giá: 1308 (rất tốt)

Đã gửi 26 March 2007 - 12:36 AM

Để đơn giản về ý tưởng viết tut. Hoành có thể viết bải giải thích các lisp đã post trên diễn đàn. Như vậy anh dễ theo dõi mà lại mang thống nhất của diễn đàn. ý kiến của em có hợp lý không
  • 1

#11 Nguyen Hoanh

Nguyen Hoanh

    biết lệnh adcenter

  • Moderator
  • PipPipPipPipPipPipPip
  • 4105 Bài viết
Điểm đánh giá: 4495 (đỉnh cao)

Đã gửi 26 March 2007 - 04:34 PM

Ok.

Có lẽ làm thế sẽ dễ hơn cho người viết, cũng dễ hơn cho người học.

thanks.
  • 2

#12 Nguyen Hoanh

Nguyen Hoanh

    biết lệnh adcenter

  • Moderator
  • PipPipPipPipPipPipPip
  • 4105 Bài viết
Điểm đánh giá: 4495 (đỉnh cao)

Đã gửi 27 March 2007 - 11:15 AM

Mổ xẻ lệnh ẩn đối tượng.
Lệnh ẩn đối tượng này là dựa trên nguyên tắc điều khiển mã dxf 60 của đối tượng. Mã này kiểm soát sự ẩn, hiện của đối tượng. Nếu đối tượng có mã dxf 60 bằng 1 có nghĩa là ẩn, 0 có nghĩa là hiện.

Để gán mã dxf 60 bằng 1 cho đối tượng (làm ẩn đối tượng), ta cần tìm xem trong mã của đối tượng đã có mã dxf 60 hay chưa? nếu có thì thay nó bằng giá trị 1, nếu chưa có thì thêm vào nó một mã dxf 60 bằng 1.

Trước tiên, chúng ta định nghĩa hàm (Dxf id obj) xem ở phần dưới. Hàm này trả về giá trị của mã id của đối tượng obj, nếu đối tượng obj không có mã id thì kết quả trả về là nil (rỗng).

Sau đó, dùng câu lệnh rẽ nhánh: nếu đối tượng có mã dxf 60 thì thay bằng giá trị mới, nếu không có thì thêm một giá trị dxf 60=1 mới vào. Câu lệnh đó như sau:
(if (dxf 60 Elem) ; kiểm tra xem có mã 60 không?
;nếu có? thay:
(entmod (subst (cons 60 1) (cons 60 (dxf 60 elem)) (entget Elem)))

;nếu không? thêm:
(entmod (append (entget Elem) (list '(60 . 1))))
)
Trong cụm lệnh trên, Elem là ent name của đối tượng cần ẩn. Hàm entmod có tác dụng modify các thuộc tính một đối tượng theo tập hợp các thuộc tính đã cho (bao gồm thuộc tính tên của đối tượng cần sửa) . Hàm entget trả về tập hợp các thuộc tính của một đối tượng. hàm subst là hàm thay giá một giá trị trong tập hợp bằng một giá trị khác. hàm append là hàm trộn hai tập hợp. Dòng lệnh (entmod (subst (cons 60 1) (cons 60 (dxf 60 elem)) (entget Elem))) để thay mã dxf 60 của đối tượng từ giá trị "(dxf 60 elem)" về giá trị 1.

Như vậy, chúng ta có thể viết được một hàm đơn giản để ẩn một đối tượng:
(defun c:ivone()
(defun Dxf (Id Obj) ;định nghĩa hàm dxf
(cdr (assoc Id (entget Obj)))
);end Dxf
(setq elem (car (entsel "\nDoi tuong can an: "))) ;chọn đối tượng
(if (dxf 60 Elem) ; kiểm tra xem có mã 60 không?
;nếu có? thay:
(entmod (subst (cons 60 1) (cons 60 (dxf 60 elem)) (entget Elem)))
;nếu không? thêm:
(entmod (append (entget Elem) (list '(60 . 1))))
)
)

Và hàm ẩn nhiều đối tượng sẽ như sau (xin xem thêm ở phần tutor tính diện tích để biết cách biến cách làm 1 đối tượng thành nhiều đối tượng):
(defun c:InVis (/ SSet Count Elem)
(defun Dxf (Id Obj)
(cdr (assoc Id (entget Obj)))
);end Dxf
(prompt "\nSelect object(s) to hide: ")
(cond
((setq SSet (ssget))
(repeat (setq Count (sslength SSet))
(setq Count (1- COunt)
Elem (ssname SSet Count))
(if (/= 4 (logand 4 (Dxf 70 (tblobjname "layer" (Dxf 8 Elem)))))
(if (Dxf 60 Elem)
(entmod (subst '(60 . 1) (assoc 60 (entget Elem)) (entget Elem)))
(entmod (append (entget Elem) (list '(60 . 1))))
)
(prompt "\nEntity on a locked layer. Cannot hide this entity. ")
);end if
);end repeat
)
);end cond
(princ)
);end c:InVis
  • 10

#13 ts088

ts088

    biết zoom

  • Members
  • Pip
  • 18 Bài viết
Điểm đánh giá: 4 (bình thường)

Đã gửi 06 August 2007 - 12:42 PM

Hay lắm. pac Hoanh qủa là 1 cao thủ. Thank pac.
  • 0

#14 ngotunghp

ngotunghp

    biết pan

  • Members
  • Pip
  • 9 Bài viết
Điểm đánh giá: 11 (tàm tạm)

Đã gửi 30 August 2007 - 10:21 AM

Bạc làm ơn viết thành 1 cuốn ebook rùi share cho anh em , chứ đọc tức bài 1 như nầy ức chế làm bác ah ...
HIHI . Cảm ơn pro .
  • 0

#15 dodeca

dodeca

    biết pan

  • Members
  • Pip
  • 5 Bài viết
Điểm đánh giá: 1 (bình thường)

Đã gửi 30 August 2007 - 09:10 PM

Làm sao để khi mình bấm chuột phải thì thoát hoàn toàn khỏi vòng lặp repeat của chương trình viết bằng Lisp. Bác nào chỉ hộ mình nha.
  • 0

#16 Nguyen Hoanh

Nguyen Hoanh

    biết lệnh adcenter

  • Moderator
  • PipPipPipPipPipPipPip
  • 4105 Bài viết
Điểm đánh giá: 4495 (đỉnh cao)

Đã gửi 31 August 2007 - 05:41 AM

Làm sao để khi mình bấm chuột phải thì thoát hoàn toàn khỏi vòng lặp repeat của chương trình viết bằng Lisp. Bác nào chỉ hộ mình nha.

Chỉ có thoát khỏi cả chương trình lisp (bằng lệnh exit hoặc quit) chứ không thể chỉ thoát khỏi lặp repeat.
Hơn nữa, để thoát khỏi một vòng lặp bằng cách bấm chuột phải còn phụ thuộc vào đoạn mã của bạn, có thể được và cũng có thể không được.
  • 1

#17 dodeca

dodeca

    biết pan

  • Members
  • Pip
  • 5 Bài viết
Điểm đánh giá: 1 (bình thường)

Đã gửi 31 August 2007 - 11:36 AM

Chỉ có thoát khỏi cả chương trình lisp (bằng lệnh exit hoặc quit) chứ không thể chỉ thoát khỏi lặp repeat.
Hơn nữa, để thoát khỏi một vòng lặp bằng cách bấm chuột phải còn phụ thuộc vào đoạn mã của bạn, có thể được và cũng có thể không được.

Bác có thể cho một ví dụ cụ thể trường hợp nào có thể thoát khỏi vòng lặp hoặc cả chương trình không? Cám ơn trước nha.
  • 1

#18 Nguyen Hoanh

Nguyen Hoanh

    biết lệnh adcenter

  • Moderator
  • PipPipPipPipPipPipPip
  • 4105 Bài viết
Điểm đánh giá: 4495 (đỉnh cao)

Đã gửi 31 August 2007 - 12:01 PM

Bác có thể cho một ví dụ cụ thể trường hợp nào có thể thoát khỏi vòng lặp hoặc cả chương trình không? Cám ơn trước nha.


bạn dùng vòng lặp while, lệnh test dưới đây sẽ yêu cầu người sử dụng nhập một chuỗi và thông báo chuỗi đó. Lặp đi lặp lại cho đến khi người dùng nhấn enter, hoặc nhập vào chuỗi có giá trị là thoat:

(defun c:test()
(setq lanthu 0)
(while (and
(setq s (getstring "\nVao chuoi: "))
(/= s "")
(/= s "thoat")
)
(alert s)
)
(princ)
)
  • 2

#19 dodeca

dodeca

    biết pan

  • Members
  • Pip
  • 5 Bài viết
Điểm đánh giá: 1 (bình thường)

Đã gửi 08 September 2007 - 07:12 PM

Bác NguyenHoanh oi! Để lưu thông tin cua line vào biến thì làm như thế nào.
  • 0

#20 Nguyen Hoanh

Nguyen Hoanh

    biết lệnh adcenter

  • Moderator
  • PipPipPipPipPipPipPip
  • 4105 Bài viết
Điểm đánh giá: 4495 (đỉnh cao)

Đã gửi 08 September 2007 - 10:49 PM

Bác NguyenHoanh oi! Để lưu thông tin cua line vào biến thì làm như thế nào.

Thông tin gì bạn?
Line có rất nhiều thông tin!
  • 2