Beruflich Dokumente
Kultur Dokumente
ID: CP11-2 Course Outline: AutoLISP has been around for a long time and has always separated the green thumbs from the gurus. This course begins by debunking some popular rumors that AutoLISP is going away and that AutoCAD VBA is better than AutoLISP. The amount of AutoLISP code used in CAD-dependent industries today remains tremendous, and AutoLISP is more powerful today than ever. Its free, it's gentle and forgiving, and it provides users with the ability to create new AutoCAD commands in minutes. This class helps seasoned AutoCAD users enter the world of customization and programming using AutoCAD software's native graphical language. The class provides over 30 examples of code to help you understand the syntax of this language. Take the AutoLISP plunge!
Fundamentals of AutoLISP
Hi, my name is dave espinosa-aguilar, and Ive been using and training folks on AutoCAD since the mid 1980's when the program came out. I work with about 125 companies on average a year doing training and custom programs for them, and I get to see how a lot of people throughout the country use the program in new and creative ways. Ive also been programming in AutoCAD since it was first possible. AutoLISP has been around for some time now, and it really separates the greenthumbs from the gurus. Its free, and it provides users the ability to create their own AutoCAD functions in minutes. Its very forgiving too (it wont destroy your operating system unless youre trying hard enough), and its a graphical language which works natively with AutoCAD entities. In other words, the more you know about basic AutoCAD usage, the more powerful your AutoLISP programming becomes!
Exercise #1: math heirarchies Reduce (+ 3 (* 4 (/ 4 (- 10 8)) 3)) (+ 3 (* 4 (/ 4 2) 3)) (+ 3 (* 4 2 3)) (+ 3 24) 27 Exercise #2: math functions Reduce (sqrt (+ 4 (* (+ 2 4)(/ 10 (sqrt (+ 4 (* 6 2))) (sqrt (+ 4 12)) (sqrt 16) 4 Exercise #3: fix/float, atoi/atof, rtos/itoa, list/car/cadr/caddr/cdr If (setq a 10.5), what does (setq b (- a (fix a))) do? 5))))
(setq b (- 10.5 10)) (setq b 0.5) lists/trains analogy (car extracts the engine, cdr extracts everything but the engine) building lists (cons adds a new engine to the train)
Exercise #4: list manipulation, cdr If (setq list1 (list 1 2 3 4 5)), what does (cddr list1) produce? (cddr list1) = (cdr (cdr list1)) = (cdr (2 3 4 5)) = (3 4 5) Exercise #5: reverse, point list, car If (setq p1 (list 1.0 2.0 3.0)) what value does (car (reverse p1)) yield? (car (3.0 2.0 1.0)) = 3.0 Exercise #6: list construction If !list1 produces (1.0 2.0 3.0), how do you place 4.0 in list1 after 3.0? (setq list1 (reverse list1)) (setq list1 (cons 4.0 list1)) (setq list1 (reverse list1)) or (setq list1 (reverse (cons 4.0 (reverse list1)))) Exercise #7: (command): work this BLINDLY (create a new yellow layer in your mind) Create a new layer called "WALL" which has a yellow color. (command "layer" "m" "wall" "c" "2" "wall" "") Exercise #8: pi What is a radian? Write a program which converts any angle in degrees to a value in radians.
180.0 ----pi
d = --r
so:
Exercise #9: defun setvar getpoint, princ Write a program which draws a line @4,3 from any selected point. (defun C:DRAWIT () (setvar "CMDECHO" 0) (setq pa (getpoint)) (command "line" pa "@4,3" "") (princ)
) Exercise #10: string editing (strcat, getstring), \n is new line Write a program which asks for first name, second name and returns both as one string. (setq first (getstring "\nEnter first name: ")) (setq second (getstring "\nEnter last name: ")) (setq third (strcat first " " second)) Exercise #11: (findfile) and SHELL verification Write a program to test the existence of a file (specified during class) (setq f (findfile "/autoexec.bat")) truth conditions: (= 1 1)...T (= 1 0)... nil (/= 1 1)... nil (/= 1 0)... T (> 2 1)... T (>= 2 1)... T (= w w)... T (= w x)...nil (and (= 1 1) (= 2 2))... T (or (= 1 1) (= 1 2))... T (= 1 1.0)... T (= 1 1.01)... nil (/= 1 1.0)... nil (/= 1 1.01)... T (>= 1 1)... T (<= 1 1)... T (= Hi hi)...nil (= Hi Hi)... T (and (= 1 1) (= 1 2))... nil (or (= hi hi) (= 1 1))... nil
while loops and counters (notice 10 still reports because there is no (princ): (setq counter 0) (while (< counter 10) (prompt (itoa counter)) (setq counter (+ counter 1)) ) if/then statements: (setq counter 0) (while (< counter 10) (prompt (itoa counter)) (setq counter (+ counter 1)) (if (> counter 5) (prompt B) (prompt A)) ) ASCII text file I/O: (read-line), (write-line), (open), (close), and UNIX file notation: (defun C:TXTWRITE () (setvar "cmdecho" 0) (setq txt (getstring T "\nEnter a word: ")) (setq f (open "c:\\test.txt" "w")) (write-line txt f) (close f) (princ) )
(defun C:TXTAPPEND () (setvar "cmdecho" 0) (setq txt (getstring T "\nEnter a word: ")) (setq f (open "c:\\test.txt" "a")) (write-line txt f) (close f) (princ) ) (defun C:TXTREAD () (setvar "cmdecho" 0) (setq f (open "c:\\test.txt" "r")) (setq txt (read-line f))
(while (/= nil txt) (prompt txt) (setq txt (read-line f)) ) (princ)
analyzing a text string (10,10,0) with (substr) and (strlen) and CDF notation: |1|2|3|4|5|6|7| (strlen string) is 7 |1|0|,|1|0|,|0| x=substr(string,1,3-1), y=substr(string,4,6-4), z=substr(string,7) Exercise #12: (progn) ,flags and leaving (command)s hanging in midstream: Draw a polyline from all points defined in text file c:\\points.dat: 10,10,0 20,20,0 30,20,0 40,50,0 (defun C:PLIMP () (setvar "cmdecho" 0) (command "pline") (setq f (open "c:\\points.dat" "r"))(setq txt (read-line f)) (while (/= nil txt) (setq ca 1 ta (strlen txt) flag 0 cb 1) (while (< ca ta) (setq test (substr txt ca 1)) (if (and (= test ",")(= flag 1)) (progn (setq z (substr txt (+ ca 1))) (setq y (substr txt cb (- ca cb))) (setq ca ta) )) (if (and (= test ",")(= flag 0)) (progn (setq x (substr txt 1 (- ca 1))) (setq flag 1) (setq cb (+ ca 1)) )) (setq ca (+ ca 1)) ) (command (list (atof x) (atof y) (atof z))) (setq txt (read-line f)) ) (command "")(close f)(princ) ) Exercise #13: cond, initget, getkword, crammed code Write a program which offers the user three options and returns a different response for each option. (defun C:CHOICE () (setvar "cmdecho" 0)(initget "A B C")(setq answer (getkword "\nChoice A/B/<C>: "))(if (= nil answer) (setq answer "C"))(cond ((= answer "A") (prompt "\nYou picked A."))((= answer "B") (prompt "\nYou picked B."))((= answer "C") (prompt "\nYou Picked C.")))(princ)) Exercise #14: groups in AutoCAD in the early 90s using (setq sa (ssget)), ssget, strcat Write a program to select several objects and move them a specified magnitude and angle: (defun C:SSMOVE () (setvar "cmdecho" 0) (setq sa (ssget) d1 (getstring "\nEnter distance: ") a1 (getstring "\nEnter angle: ") ) (command "move" sa "" "0,0" (strcat "@" d1 "<" a1)) (princ) )
Exercise #15: entsel, entget, assoc (explain dotted pairs), ssget x mode: Write a program to pick an entity and delete all entities on it layer. (use WILHOME to test) (defun C:LAYDEL () (setvar "cmdecho" 0) (setq enta (car (entsel "\nPick entities for layers to delete: "))) (while enta (setq ea (entget enta) laya (cdr (assoc 8 ea))) (setq sa (ssget "x" (list (cons 8 laya)))) (command "erase" sa "") (setq enta (car (entsel "\nPick entities for layers to delete: "))) )(princ) ) Exercise #16: getvar, subst: Write a program to change a picked entity's layer to the current layer: (defun C:CLAY () (setvar "cmdecho" 0) (setq lay (getvar "CLAYER")) (setq enta (car (entsel "\nPick entity: ")) ea (entget enta) ) (setq ea (subst (cons 8 lay) (assoc 8 ea) ea)) (entmod ea)(princ) ) ;write a routine to export selected TEXT to ASCII file for MTEXT import! (defun C:EXPTEXT () (setvar cmdecho 0) (setq tlist nil) (setq enta (car (entsel \nPick TEXT entity: ))) (while enta (setq ea (entget enta) txta (cdr (assoc 1 ea)))(setq tlist (cons txta tlist)) (setq enta (car (entsel \nPick next TEXT entity: ))) )(princ) ) Exercise #17: subst, entmod Write program to acquire the layer of one entity and make it the layer of a selected entity. (defun C:CHLAY () (setvar "cmdecho" 0) (setq enta (car (entsel "\nPick layer-defining entity: "))) (setq ea (entget enta) laya (cdr (assoc 8 ea))) (setq entb (car (entsel "\nPick entity to be changed: "))) (setq eb (entget entb) eb (subst (cons 8 laya) (assoc 8 eb) eb) ) (entmod eb) (princ) ) Exercise #18: car, cdr, point lists, grdraw Write a program which gets two diagonally opposed points and draws a box from them using the LINE command. (defun C:LINEBOX () (setvar "cmdecho" 0) (setq p1 (getpoint "\nPick first point: ") p2 (getpoint p1 "\nPick second point: ") ) (grdraw p1 p2 1 1) (setq p3 (list (car p1) (cadr p2)) p4 (list (car p2) (cadr p1)) ) (command "line" p1 p3 p2 p4 p1 "") (princ)
) Exercise #19: polar, distance, angle Write a program which uses three point picks to create an island tag. (defun C:ISLE () (setvar "cmdecho" 0) (setq pa (getpoint "\nBeginning point: ") pb (getpoint pa "\nOrientation point: ") ) (grdraw pa pb 1 1) (setq pc (getpoint pa "\nRadius point: ")) (grdraw pa pb 1 1) (setq aa (angle pa pb) ab (+ aa (* pi 0.5)) da (distance pa pc) p1 (polar pa ab da) p2 (polar pa (+ ab pi) da) p3 (polar pb ab da) p4 (polar pb (+ ab pi) da) ) (command "pline" p2 p4 "a" p3 "l" p1 "a" p2 "cl") (princ) ) Exercise #20: write-line ssname sslength Extract selected text entities to an ASCII text file. (defun C:EXTXT () (setvar "cmdecho" 0) (setq tfile (getstring "\nEnter file name: ")) (setq f (open tfile "w")) (setq sa (ssget)) (setq ca 0 ta (sslength sa)) (while (< ca ta) (setq enta (ssname sa ca) ea (entget enta) txta (cdr (assoc 1 ea))) (write-line txta f) (setq ca (+ ca 1)) ) (close f) (princ) ) Exercise #21: ssget "x", (list entname point) entries Write a program which converts all lines in a drawing to polylines with a thickness of .5: (defun C:LWID () (setvar "cmdecho" 0)(setq sa (ssget "x" (list (cons 0 "LINE")))) (if sa (progn (setq ca 0 ta (sslength sa)) (while (< ca ta) (setq enta (ssname sa ca) ea (entget enta) pa (cdr (assoc 10 ea))) (command "pedit" (list enta pa) "y" "w" "0.5" "x") (setq ca (+ ca 1)) ) )) (princ) ) create a 1-attribute block and insert it several times with different numeric values for the attribute value. use the routine below with them to test it.
Exercise #22: Create selection set of several blocks by a specified attribute value. (defun C:SSATT ()
(setvar "cmdecho" 0) (setq test (getstring "\nEnter attribute value: ")) (setq sa (ssget "x" (list (cons 0 "INSERT")(cons 2 "THING")))) (setq sb nil sb (ssadd)) (if sa (progn (setq ca 0 ta (sslength sa)) (while (< ca ta) (setq enta (ssname sa ca)entb (entnext enta)) (setq eb (entget entb) testb (cdr (assoc 1 eb))) (if (= testb test) (ssadd enta sb)) (setq ca (+ ca 1)) ) )) (command "move" sb "" "0,0" "0,0") (prompt "\nBlocks are in previous group.") (princ) ) Exercise #23: <= getint, rtos Write a program which creates numerically incremented text entities with prefixes and suffixes. (defun C:ITEXT () (setvar "cmdecho" 0) (setq ta (getreal "\nEnter text height: ") na (getint "\nEnter beginning number: ") nb (getint "\nEnter increment step: ") pref (getstring "\nEnter prefix: ") suff (getstring "\nEnter suffix: ") ) (setq pa (getpoint "\nPick point: ")) (while (/= nil pa) (setq tstr (strcat pref (itoa na) suff)) (command "text" "j" "m" pa (rtos ta 2 2) "0" tstr) (setq na (+ na nb)) (setq pa (getpoint "\nPick point: ")) ) (princ) ) Exercise #24: getstring T, drawing technique Write a program which creates a tag with superscript, subscript and cross-section tag.
(defun C:TAG () (setvar "cmdecho" 0) (setq pa (getpoint "\nPick tag centerpoint: ") pb (getpoint pa "\nPick tag cross-section/orientation point: ") aa (angle pa pb) ) (grdraw pa pb 1 1) (setq pc (getpoint pa "\nPick tag radial point: ") dc (distance pa pc) suptxt (getstring T "\nEnter superscript text: ")
subtxt (getstring T "\nEnter subscript text: ") ) (command "circle" pa pc) (command "line" (polar pa pi dc) (polar pa 0.0 dc) "") (setq pd (polar pa aa dc) pe (polar pb (+ aa (* pi 0.5)) dc)) (command "line" pd pb pe "") (command "text" "j" "m" (polar pa (* pi 0.5)(* dc 0.5))(* dc 0.5) "0.0" suptxt) (command "text" "j" "m" (polar pa (* pi 1.5) (* dc 0.5))(* dc 0.5) "0.0" subtxt) (princ) ) Exercise #25: subst Write a program which "glues together" two separate pieces of text. (defun C:TGLUE () (setvar "cmdecho" 0) (setq enta (car (entsel "\nPick first string: ")) ea (entget enta) txta (cdr (assoc 1 ea)) entb (car (entsel "\nPick second string: ")) eb (entget entb) txtb (cdr (assoc 1 eb)) txtc (strcat txta txtb) ea (subst (cons 1 txtc) (assoc 1 ea) ea) ) (entdel entb)(entmod ea) (princ) ) Exercise #26: eval, max, length Write a program which sorts the list ("70" "32" "65" "13" "162") from miminum to maximum value. (defun C:ASCSORT () (setvar "cmdecho" 0) (setq list1 (list "70" "32" "65" "13" "162") list2 nil) (prompt "\nOriginal list:")(prin1 list1) (setq ca 0 ta (length list1)) (while (< ca ta) (setq tval (atoi (nth ca list1)) list2 (cons tval list2) ca (+ ca 1) ) ) (setq list1 nil list3 nil) (while (> (length list2) 0) (setq hival (eval (cons max list2))) ;trick! (setq ca 0 ta (length list2)) (while (< ca ta) (setq tval (nth ca list2)) (if (/= tval hival) (setq list1 (cons tval list1)) (setq list3 (cons tval list3)) ) (setq ca (+ ca 1)) ) (setq list2 list1 list1 nil) ) (setq ca 0 ta (length list3)) (while (< ca ta) (setq tval (itoa (nth ca list3)) list1 (cons tval list1) ca (+ ca 1) ) ) (prompt "\nFinal list:")(prin1 list1) (princ) )
Exercise #27: tables Write a program which generates a report of all blocks in a drawing. (defun C:BCOUNT () (setvar "cmdecho" 0) (setq ea (tblnext "BLOCK" T)) (while (/= nil ea) (setq bname (cdr (assoc 2 ea))) (setq sa (ssget "x" (list (cons 0 "INSERT")(cons 2 bname)))) (if sa (prompt (strcat "\n" bname ": " (itoa (sslength sa))))) (setq ea (tblnext "BLOCK")) ) (princ) ) Exercise #28: strcase, entnext Write a program which exports the values of a certain block attribute to a text file (in this case using a block called THING with the first attribute in THING being exported). (defun C:EXPATT () (setvar "cmdecho" 0) (setq tfile (getstring "\nEnter file name: ")) (setq f (open tfile "w")) (setq test (strcase (getstring "\nEnter attribute tag to export: "))) (setq sa (ssget "x" (list (cons 0 "INSERT")(cons 2 "THING")))) (setq sb nil sb (ssadd)) (if sa (progn (setq ca 0 ta (sslength sa)) (while (< ca ta) (setq enta (ssname sa ca) entb (entnext enta) ; go to the next entity after the insertion ) (setq eb (entget entb) testb (cdr (assoc 2 eb))) (while (/= testb test) (setq entb (entnext entb) eb (entget entb) testb (cdr (assoc 2 eb)) ) ) (write-line (cdr (assoc 1 eb)) f) (setq ca (+ ca 1)) ) )) (close f) (princ) )
In past years, students of this class have requested a portion of it be dedicated to the code development process. Its not enough to get the syntax under ones belt. One also needs an idea how the code evolves from idea to application. Part of the reason this process has not been examined during the class is because admittedly a lot of it is intuitive and much of it requires a solid understanding of how AutoCAD works internally. This year the tutorial instructors were given double the amount of pages they could include in their handouts, and so I chose to dedicate a significant portion of this handout to the development of an application from inception to implementation. It is told in the manner of a CAD manager talking himself thru the development process from beginning to end. As with 95% of all AutoLISP applications and utilities developed, it begins with a need that is not met by AutoCAD software out of the box...
dummy block). Text height also needs to be asked. This routine could later be modified to offset text to one side or the other of the selected polyline. Thats for another days wishlist item. Some Initial Dangers: I could just develop this routine to use the current style, but Id have to make some assumptions about that style when I use the (command) syntax since command prompt sequences change based on a Styles definition. The string of text has to physically fit along the line or else I need some way to ensure that the user is warned that it wont fit. If the text is too big, letters might overlap each other. If the text is too small, the text might look like points on top of the polyline. The Code Breakdown: The entire program POLYTEXT including its co-utility DELPOLYTEXT is provided below. Remarks are included in the code throughout to explain the thinking behind each sequence of code lines. (defun C:POLYTEXT () ;don't report the utility functions as it runs aesthetically pleasing (setvar "cmdecho" 0) ;get the text height as a real number (setq txt (getstring T "\nEnter the string to drape along the polyline: ")) (setq pa (getpoint \nPick 2 points to establish text height: )) (setq pb (getpoint pa) txth (distance pa pb)) ;establish number of characters in string (setq txtlen (strlen txt)) ;get the dedicated layer name, make it with color 1 and set it to current (setq laynam (getstring "\nEnter dedicated layer name: ")) (command "layer" "m" laynam "c" "1" laynam "") ;get the polyline entity list information (setq enta (car (entsel "\nPick lightweight polyline: "))) (setq ea (entget enta)) ;count how many items are in the polyline entity list and initialize a counter (setq ca 0 ta (length ea)) ;cycle thru all items in the polyline list until you find the first 10 group ;which is the starting vertex of the polyline. create a lightweight polyline and ;(entget (car (entsel))) it to see a typical list for reference (setq test (car (nth ca ea))) (while (/= test 10) (setq ca (+ ca 1)) (setq test (car (nth ca ea))) ) ;create a 3D point from the first vertex of the polyline ;remember that a lightweight only has 2D coordinates -- add a 0.0 for z (setq xa (cadr (nth ca ea))) (setq ya (caddr (nth ca ea))) (setq pa (list xa ya 0.0)) ;establish the character separation distance from the first vertex (setq pb (getpoint pa "\nPick character separation distance point: ")) (setq da (distance pa pb)) ;check to see if this will physically work. ; ; (distance between characters) x (number of characters) = ? ; polyline length = ? ; ;if polyline length is less than (char)x(dist) figure, shut down application ;get polyline length by LISTing it and getting PERIMETER system variable
;use (graphscr) to return to graphics mode after LIST command used (setq test1 (* da txtlen)) (command "list" enta "")(graphscr) (setq test2 (getvar "perimeter")) (if (< test1 test2) (progn ; create a dummy block with the same name as dedicated layer at first vertex (command "text" "j" "bc" pa txth "0" "I") (setq entb (entlast)) (command "block" laynam pa entb "") ;remember last entity in database before draping new blocks so that you can ;go back to it later on and find insertion points and rotations of all entities ;that follow it (recently created block insertions) (setq entb (entlast)) ;drape block along the polyline using MEASURE (command "measure" enta "b" laynam "y" da) ;drap each block for a character using intersion point and rotation info (setq ca 0) (while (< ca txtlen) (setq entb (entnext entb) eb (entget entb)) (setq pa (cdr (assoc 10 eb)) ra (cdr (assoc 50 eb))) (command "text" "j" "bc" pa txth (angtos ra 2 2) (substr txt (+ ca 1) 1)) (setq ca (+ ca 1)) ) ;delete all dummy blocks (setq sa (ssget "x" (list (cons 0 "INSERT")(cons 2 laynam)))) (command "erase" sa "") ) ; finish text-fits condition (prompt "\nThis text won't fit using this spacing.") ; warn text doesnt fit ) ; finish entire if statement (princ) )
(defun C:DELPOLYTEXT () (setvar "cmdecho" 0) ;get the layer name of the selected character (setq enta (car (entsel "\nPick polytext character to delete: "))) (setq ea (entget enta) laynam (cdr (assoc 8 ea))) ;erase all entities on that layer (setq sa (ssget "x" (list (cons 8 laynam)))) (command "erase" sa "") ;purge the block (command "purge" "b" laynam "n") ;purge the layer after setting current layer to "0" incase (command "layer" "s" "0" "") (command "purge" "la" laynam "n") ) (princ)