In the last lesson, we found out how to give an AutoLISP program a name, save it to disk, and have it automatically loaded when AutoCAD starts up:
(defun c:label ( / xyz) (setq xyz (getpoint "Pick point: ")) (command "text" xyz 200 0 xyz) )
In this lesson, we begin to worry about user interface enhancements and learn about the Car, Cdr, RtoS, and StrCat functions.
If you recall, I had thought up a wishlist for the point labelling program. Last lesson, I addressed the first three items on the wishlist. The remaining items include:
#4. The x,y,z coordinates are printed to eight decimal places, which, for most users, that's way too many.
#5. You may want to control the layer that the text is placed on.
#6. You may want a specific text style.
#7. Certainly, you would like some control over the size and orientation of the text.
#8. Here's an orthogonal idea: store the x,y,z-coordinates to a file on disk -- just in case you ever want to reuse the data.
While all those features sound desirable, they may make the program less desirable -- a case of anti-synergy (the sum of the whole being less than the sum of the parts). Can you image how you irritated you'd get if you had to answer the questions about decimal places, text font, text size, text orientation, layer name, filename -- each time you wanted to label a single point? Last lesson, I had argued that the first three items are important to add to our AutoLISP code. Let's see how we can deal with the other five items:
The x,y,z coordinates are printed to eight decimal places --that's too many. There are two solutions. One is to ask the user the number of decimal places, as shown by the following code fragment:
Command: (setq uprec (getint "Label precision: "))
Label precision: 1 1
Or steal the value stored in system variable LUPrec -- the precision specified by the user via the Units and DdUnits commands -- under the (not necessarily true) assumption that the user want consistent units. The code to do this is as follows:
(setq uprec (getvar "LUPREC"))
That was the easy part. The tough part is applying the precision to the x,y,z coordinates, which takes three steps: (1) pick apart the coordinate triplet; (2) apply the precision factor; and (3) join the coordinates together again. Here's how:
1. Open Label.Lsp in NotePad or any other text editor. Remove / xyz from the code. This makes the variable "global," so that I can check its value at AutoCAD's Command: prompt. The code should look like this:
(defun c:label ( ) (setq xyz (getpoint "Pick point: ")) (command "text" xyz 200 0 xyz) )
Save and load Label.Lsp into AutoCAD.
2. Run Label.Lsp, picking any point on the screen. If you don't see the coordinates printed on the screen, use the Zoom Extents command.
3. At the Command prompt, type the following:
Command: !xyz
(6.10049 8.14595 10.0)
The exclamation mark forces AutoCAD to print the value of variable XYZ, which holds the x,y,z coordinates. Your results will differ, depending on where you picked.
4. LISP has several functions for picking apart a list. Here I'll use the Car and Cdr functions, and combinations thereof. The Car function extracts the first item (the x-coordinate) in the list. Try it now:
Command: (car xyz)
6.10049
5. The Cdr function is the compliment to Car. It removes the first item from the list and gives you what's left:
Command: (cdr xyz)
(8.14595 10.0)
6. In addition to Car and Cdr, LISP allows me to combine the "a" and "d" in several ways to extract other items in the list. To extract the y-coordinate, use Cadr, as follows:
Command: (cadr xyz)
8.14595
7. And to extract the z-coordinate, use Caddr, as follows:
Command: (caddr xyz)
8.14595
8. I now have a way to extract the x-coordinate, the y-coordinate, and the z-coordinate from variable XYZ. I'll store them in their own variables, as follows:
Command: (setq ptx (car xyz)
1> pty (cadr xyz)
1> ptz (caddr xyz)
1> )
I am using PtX to store the x-coordinate, PtY for the y-coordinate, etc. In addition, I am using a form of LISP shorthand that let's me apply the SetQ function to several variables. Recall the reason for AutoCAD's 1> prompt: it reminds me that a closing parenthesis is missing.
9. Now that the three coordinates are separated, I can finally reduce the number of decimal places. There are a couple of ways to do this. I'll use the RtoS function because it does two things at once:
(1) changes the number of decimal places to any number between 0 and 8; and
(2) converts the real number into a string. Why? You'll see later. For now, here is the RtoS function at work:
Command: (rtos ptx 2 uprec)
"6.1"
The RtoS function uses three parameters:
Mode | Units |
1 | Scientific |
2 | Decimal |
3 | Engineering |
4 | Architectural |
5 | Fractional |
Assuming, then, that the precision in UPrec is 1, the RtoS function in the code fragment above reduces 6.10049 to 6.1.
10. I truncate and preserve the values of x, y, and z, as follows:
Command: (setq ptx (rtos ptx 2 uprec)
1> pty (rtos pty 2 uprec)
1> ptz (rtos ptz 2 uprec)
1> )
Notice that I can set a variable equal to itself: the first PtX holds the new value of the x-coordinate after RtoS gets finished processing the second PtX. Reusing a variable name like this helps conserve memory.
11. With the coordinates truncated, I now have to string (pardon the pun) them together with the StrCat function, short for string concatenation. Try it now:
Command: (strcat ptx pty ptz)
"6.18.110.0"
12. Oops! Not quite the look I was hoping for. Since LISP can't know when I want spaces, it doesn't provide any. I have to insert them. StrCat is one of the most useful LISP functions, since it lets me create a string that contains text and variables, like this:
Command (setq xyz (strcat ptx ", " pty ", " ptz))
"6.1, 8.1, 10.0"
That's more like it.
13. Back to the text editor. Add in the code I developed here:
Consider order of appearance of variables (being localised) in program...
(defun c:label ( / uprec xyz ptx pty ptz xyz1)
(defun c:label ( / xyz xyz1 uprec ptx pty ptz) (setq uprec (getint "Label precision: ")) (setq xyz (getpoint "Pick point: ")) (setq ptx (car xyz) pty (cadr xyz) ptz (caddr xyz) ) (setq ptx (rtos ptx 2 uprec) pty (rtos pty 2 uprec) ptz (rtos ptz 2 uprec) ) (setq xyz1 (strcat ptx ", " pty ", " ptz)) (command "text" xyz 200 0 xyz1) )
Notice that I made all the variables local. Notice, too, the change to variable Xyz in the last couple of lines: I don't want the text placed at the rounded-off coordinates, so I use Xyz1 as the variable holding the text string.
14. Finally, I should add comments to my code to remind me what it does when I look at the code several months from now. The semi-colon indicates the start of a comment:
; Label.Lsp labels a picked point with its x,y,z-coordinates. ; by Ralph Grabowski, 25 February, 1996. ; ;User Input ; uprec (integer) "User Precision?" No. of decimal places specified by user. ; xyz (real) The point triplet picked (or entered?) by user. ;Calculated Values ; ptx, pty, ptz (string) The point triplet real values individual converted to string values. ; xyz1 (string) Concatenated string values in triplet format to print on screen (or to a file) ; (defun c:label ( / uprec xyz ptx pty ptz xyz1) ;user input (setq uprec (getint "Label precision: ")) ;number of decimal places: (setq xyz (getpoint "Pick point: ")) ;pick point on-screen for lable ;calcs ;separate 3D point into individual real x,y,z values (setq ptx (car xyz) pty (cadr xyz) ptz (caddr xyz) ) ;convert real x,y,z values to string in sci format to specified no. of decimal places (setq ptx (rtos ptx 2 uprec) pty (rtos pty 2 uprec) ptz (rtos ptz 2 uprec) ) ;Recombine (concatenate) individual string values into a (string) 3D point: (setq xyz1 (strcat ptx ", " pty ", " ptz)) ;output text to screen ;use text command to place the truncated 3D point string on the user pick point (command "text" xyz 200 0 xyz1) ;note: height and angle are default offers only )
15. Save the file as Label.Lsp, then load the AutoLISP routine into AutoCAD with:
Command: (load "label")
"C:LABEL"
16. Run the routine:
Command: label
Label precision: 1
Pick point: [pick]
text Justify/Style/<Start point>:
Height <200.0000>: 200
Rotation angle <0>: 0
Text: 5012.3, 773.2, 0.0
Command: nil
Comments on this tutorial series? Tell me about it.
Return to home page.