Chapter 2: Storing and Running Programs
|
|
In the last chapter, you learned how to use the interpreter and in the process, you were introduced to some of the basic concepts of AutoLISP. You can now enter simple expressions into the interpreter to perform specific tasks. But once you exit the drawing editor, all of your work in AutoLISP is lost. AutoLISP would be difficult to use if there weren't some way of storing your functions for later retrieval. It would be especially difficult to create complex programs if you could only load them into the interpreter from the keyboard one line at a time. In this chapter, you will explore the development of programs through the use of AutoLISP files and in the process, review the AutoLISP concepts you learned in chapter 1.
Instead of entering all of your functions directly into the interpreter, you have the option of writing them in a text file outside of AutoCAD. Later, when you want to use your function, you can quickly load and run them using the AutoLISP Load function. Functions you store and load in this way will act just as if you entered them into the interpreter manually. Since you can easily edit and review your functions in a word processor, you can begin to develop larger, more complex functions and programs.
Before you can create an AutoLISP file, you need a word processor that will read and write ASCII files. ASCII stands for American Standard Code for Information Interchange. As the name implies, ASCII format was created to allow different computing systems to exchange data with each other. Most word processors allow you to generate files in this format. In this and the preceding chapters, whenever we say to open or create an AutoLISP file, we are asking you to open an ASCII file using your word processor. You can use the Windows Notepad to do most of your AutoLISP work. Most other word processors will also save and read ASCII files, usually called TXT files in Windows.
The most common way to store AutoLISP programs is to save it as a text file with the extension .lsp. This file should contain the same information you would enter through the keyboard while using the interpreter interactively. We suggest that you create a directory called /LSP in which you can store all of your AutoLISP programs. By keeping your programs together in one directory, you are able to manage them as their numbers grow.
The program listed in figure 2.1 combines many of the concepts you have learned in chapter one into a single AutoLISP program. You will create a file containing this program then load and run the program into AutoCAD to see how it works. We use the term program to describe a function that performs a task when entered at the AutoCAD command prompt, even though programs such as the one in Figure 2.1 can still be considered functions
(defun c:BOX ( / pt1 pt2 pt3 pt4 ) (setq pt1 (getpoint "Pick first corner: ")) (setq pt3 (getcorner pt1 "Pick opposite corner: ")) (setq pt2 (list (car pt3) (cadr pt1))) (setq pt4 (list (car pt1) (cadr pt3))) (command "line" pt1 pt2 pt3 pt4 "c" ) )
Figure 2.1: A program to draw boxes
1. Use the Windows notepad to create a new text file called Box1.lsp
2. Carefully enter the first line from figure 2.1. Or you can simply cut and paste the data from this document into the Box1.lsp file.
(defun C:BOX (/ pt1 pt2 pt3 pt4) Be sure you have entered everything exactly as shown before you go to the next line. Pay special attention to the spaces between elements of the line. Also note the capitalization of letters. Capitalization is not particularly important at this point however.
3. Press return to move to the next line.
4. Carefully, enter the second line again checking your typing and the spacing between elements before go to the next line. Also be sure to use the Shift-Apostrophe key for the double quotes, do not use two apostrophes.
5. Continue entering each line as described in the previous steps. When you are done, double check your file for spelling errors and make sure the parentheses are balanced. Save and exit the Box1.lsp file. You now have a program that you can load and run during any AutoCAD editing session.
To load an AutoLISP file you use an AutoLISP
1.Start AutoCAD and create a new file called Box1.
2. When you are in the drawing editor, enter the following at the command prompt:
(load "box") If the Box.lsp file is in a directory other than the current directory, the \lsp directory for example, enter
(load "/lsp/box")
The box program is now available for you to run.
As you can see, the Load function is used like any other AutoLISP function. It is the first element of an expression followed by an argument. The single argument to the load function is always a string value. Notice that within the string in the above example, the forward slash sign is used to designate a directory instead of the usual backslash. This is important to keep in mind as it is a source of confusion to both novice and experienced AutoLISP users. AutoLISP uses the backslash to denote special codes within strings. Whenever AutoLISP encounters a backslash within a string, it expects a code value to follow. These codes allow you to control the display of strings in different ways such as adding a carriage return or tab. If you use the backslash to designate directories, you must enter it twice as in the following example:
(load "\\lsp\\box")
Once the file is loaded, you will get the message:
C:BOX
You may have noticed that Load uses a string data type for its argument. Just as with numbers, strings evaluate to themselves, so when AutoLISP tries to evaluate "/lsp/box" the result is "/lsp/box".
Once you have loaded the box program, you can run it at any time during the current editing session. However, once you exit AutoCAD the program is not saved with the file. You must re-load the program file in subsequent editing sessions before it can be used again. Now try running the program.
1. First, set the snap mode and the dynamic coordinate readout on.
2. Enter the word Box at the command prompt. You should get the following prompt:
Pick first corner: 3. If your screen is in text mode, use the F2 key to shift to the graphic screen. Move the cursor so that the coordinate readout reads 2.0000,3.0000 and pick that point. The next prompt appears:
Pick opposite corner: 4. Now move your cursor. A window follows the motion of your cursor (see figure 2.2). Move the corner of the window to the so that the coordinate 8.0000,6.000 is displayed on the coordinate readout then pick that point. The box is drawn and the Command prompt returns. Figure 2.3 gives you a general description of how this box program works.
Understanding How a Program Works
Up until now, you have been dealing with very simple AutoLISP expressions that perform simple tasks such as adding or multiplying numbers or setting system variables. Now that you know how to save AutoLISP code in a file, you can begin to create larger programs. The box program is really nothing more than a collection of expressions that are designed to work together to obtain specific results. In this section, we will examine the Box program to see how it works.
The Box program draws the box by first obtaining a corner point using Getpoint:
(setq pt1 (getpoint pt1 "Pick first corner: ''))
The user will see only the prompt portion of this expression:
Pick first corner:
Next, the opposite corner point is obtained using Getcorner (see Figure 2.3).
(setq pt3 (getcorner pt1 "Pick opposite corner: ''))
Again, the user only sees the prompt string:
Pick opposite corner:
Figure 2.3: The workings of the box program
You may recall that Getcorner will display a window as the user move the cursor. In this box program, this window allows the user to visually see the shape of the box before the opposite corner is selected (see figure 2.2). Once the second point is selected, the Box program uses the point coordinates of the first and opposite corners to derive the other two corners of the box. This is done by manipulating the known coordinates using Car, Cadr, and List (see Figure 2.4).
pt2 (list (car pt3) (cadr pt1)))
pt4 (list (car pt1) (cadr pt3)))
Figure 2.4: Using Car, Cadr, and List to derive the remaining box corners
Pt2 is derived by combining the X component of Pt3 with the Y component of Pt1. Pt 4 is derived from combining the X component of Pt1 with the Y component of Pt3 (see figure 2.5).
Figure 2.5: Using Car and Cadr to derive Pt2 and Pt4
Using AutoCAD Commands in AutoLISP
The last line in the box program:
(command "line'' pt1 pt2 pt3 pt4 "c'')
shows you how AutoCAD commands are used in an AutoLISP expression (see figure 2.6). Command is an AutoLISP function that calls standard AutoCAD commands. The command to be called following the Command function is enclosed in quotation marks. Anything in quotation marks after the Command function is treated as keyboard input. Variables follow, but unlike accessing variables from the command prompt, they do not have to be preceded by an exclamation point. The C enclosed in quotation marks at the end of the expression indicates a Close option for the Line command (see Figure 2.7.
Figure 2.6: Using AutoCAD commands in a function.
Figure 2.7: Variables help move information from one expression to another.
The box program is like a collection of expressions working together to perform a single task. Each individual expression performs some operation who's resulting value is passed to the next expression through the use of variables (see figure 2.8).
Figure 2.8: Arguments are assigned to variables in the function
The first line in the box program:
(defun c:BOX (/ pt1 pt2 pt3 pt4)
ties the collection of expressions that follow into a command called Box. Defun is a special function that allows you to define other functions. The arguments to defun are first the function name, in this case, C:BOX, followed by the argument list. The Quote function is automatically applied to the first argument. The c: in the name tells AutoLISP that this function is to act like an AutoCAD command. This means that if the function name is entered at the AutoCAD command prompt, the function will be executed as an AutoCAD command. The name following the C: should entered in upper case letters. Care should be take not to give your functions names reserved for AutoLISP's built in functions and atoms. If, for example, you were to give the box function the name setq, then the setq function would be replaced by the box function and would not work properly.
Table 1.1 shows a list of AutoLISP function names that can easily be mistaken for user defined variable names.
abs
if
or
and
length
pi
angle
list
read
apply
load
repeat
atom
member
reverse
distance
nil
set
eq
not
t
equal
nth
type
fix
null
while
float
open
Table 1.1 AutoLISP function names
The list that follows the name Box is an argument list. An argument list is used for two purposes. First, it is used where the function is called from another function to evaluate a set of values. For example, you could define a function that adds the square of two variables. Try entering the following function directly into the AutoLISP interpreter.
(defun ADSQUARE (x y) (+ (* x x) (* y y))
)
1. Carefully enter the first line. Pay special attention to the parentheses and spaces.
(defun ADSQUARE (x y) 2. Once you are sure everything is correct, press Return. You will see the following prompt:
1> This tells you that your expressing is missing one closing parenthesis.
3. Enter the rest of the function at the 1> prompt again checking your typing before pressing Return.
In this example, the c: is left out of the Defun line. By doing this, you create a function that can be used in other functions like a subprogram or during an AutoCAD command. We'll discuss this item in more detail later. Note that the variables X and Y are included in the parentheses after the name of the function, ADSQUARE. This is the argument list. To use this function, enter the following at the command prompt:
(adsquare 2 4)
AutoLISP returns 20. The variables X and Y in the ADDSQUARE function take on the arguments 2 and 4 in the order they are listed. X takes on the value of 2, and Y takes on the value of 4 (see figure 2.9).
Figure 2.9: Arguments are evaluated before they are assigned to variables in the function
Variables can also be used to pass values to the function. For example, if you have a variable called A whose value is 2 and a variable B whose value is 4, you could use A and B in place of the 2 and 4. Enter the following:
(setq a 2) (setq b 4)
(adsquare a b)
AutoLISP returns 20. Remember that AutoLISP evaluates the arguments before applying them to the function. This rule applies even to functions that you create yourself. In this case, the A is evaluated to 2 before it is passed to the X variable B is evaluated to 4 before it is passed to the Y variable.
Local and Global Variables
The second use for the argument list is to determine global and local variables. Global variables maintain their value even after a function has finished executing. In chapter 1, when you assign the value 1.618 to the variable Golden, Golden holds that value no matter where it is used. Any function can evaluate Golden to get its value, 1.618. Enter the following:
(setq golden 1.618) (adsquare 2 golden)
The value of 6.61792 is returned. A local variable, on the other hand, holds its value only within the function it is found in. For example, the variable X in the Adsquare function above holds the value 2 only while the Square function is evaluated. Once the function is finished running, X's value of 2 is discarded. Enter the following:
!x
Nil is returned. The variables A, B, and Golden, however, are global and will return a value. Enter the following:
!golden
The value 1.618 is returned. This temporary assigning of a value to a variable with a function is called binding. This term should not be confused with the term bound which often refers to the general assignment of a value to a variable. In the example of X above, we say that a binding is created for the value X within the function Adsquare. In order for binding to take place, a variable must be used within a function that includes that variable in its argument list. Global variables cannot have bindings since they are not by their very definition confined to individual functions (see figure 2.10).
Since an argument list is used for two purposes, the forward slash symbol is used to separate variables used locally from function arguments to which values are passed when the function is called. The variables used to hold the value of arguments are listed first. Then a slash is entered, then the list of local variables as in the following example:
(defun square2 (x y / dx dy) (setq dx (* x x))
(setq dy (* y y))
(+ dx dy)
)
X and Y are variables that will be assigned values when the function is called initially, as in the Square function given earlier. Dx and Dy, on the other hand, are variables assigned values within the function, so they follow the slash sign. In either case, the variables are local. Arguments left out of the list become global. If, for example, Dy is left out of the argument list, it's value will remain in the AutoLISP system memory for as long as the current editing session lasts, and can be retrieved through evaluation by any other function. However, all of the functions and variables you have created in this session will be lost as soon as you exit the drawing editor.
We should mention that you must include a space before and after the slash sign in the argument list. If these spaces are not present, you will get an error message.
In both the adsquare and Square2 functions above we left out the c:. As we mentioned, this allows the function to be used by other functions like a subprogram. You can also use functions defined in this way in the middle of other commands by entering the name of the function enclosed by parentheses at the command prompt. For example, you could define a function that converts centimeters to inches. Carefully enter the following function:
(defun CMTOI (cm) (* cm 0.3937)
)
Now suppose you started the Insert command to insert a symbol file into a drawing. When the prompt
X scale factor (1) / Corner / XYZ:
appears, you could enter
(cmtoi 90)
to signify that your symbol is to be given the scale of 90 centimeters.
The AutoLISP function Cmtoi will convert the 90 to 35.433 and enter this value for the X scale factor prompt. This can be quite useful where a value conversion or any other type of data conversion is wanted.
Eventually, you will find that some of your AutoLISP programs are indispensable to your daily work. You can have your favorite set of AutoLISP programs automatically load at the beginning of every editing session by collecting all of your programs into a single file called Acad.lsp. Be sure that Acad.lsp is in your AutoCAD directory. By doing this, you don't have to load your programs every time you open a new file. AutoCAD will look for Acad.LSP when it enters the drawing editor, and if it exists, AutoCAD will load it automatically.
1. Exit AutoCAD and check to see if you already have a file called Acad.lsp in your Acad directory. If so, rename it to Acadtemp.lsp.
2. Next, rename the Box.lsp file to Acad.lsp. Place Acad.lsp in your AutoCAD directory if it isn't there already.
3. Start AutoCAD and open any file. When the drawing editor loads, notice the following message in the prompt area:
Loading acad.lsp...
Now, the box program is available to you without having to manually load it.
Though the Acad.lsp file only contained the box program, you could have included several programs and functions in that single file. Then, you would have access to several AutoLISP programs by loading just one file.
As you begin to accumulate more AutoLISP functions in your ACAD.lsp file, you will notice that AutoCAD takes longer to load them. This delay in loading time can become annoying especially when you just want to quickly open a small file to make a few simple revisions. Fortunately, there is an alternative method for automatically loading programs that can reduce AutoCAD's start-up time.
Instead of placing the programs code in Acad.lsp, you can use a program that loads and runs the program in question. For example, you could have the following line in place of the box program in the Acad.lsp file:
(defun c:BOX () (load "/lsp/box") (c:box))
We will call this a box loader function. Once the above function is loaded, entering Box at the command prompt will start it. This box loader function then loads the real Box program which in turn replaces this box loader function. The (c:box) in the box loader function is evaluated once the actual box program has been loaded thus causing the box program to run. C:BOX is the symbol representing the program BOX so when it evaluated, like any function, it will run.
As you can see, this program takes up considerably less space that the actual box program and will therefore load faster at start-up time. You can have several of these loading programs, one for each AutoLISP function or program you wish to use on a regular basis. Imagine that you have several programs equivalent in size to the box program. the Acad.lsp file might be several pages long. A file this size can take 30 seconds to load. If you reduce each of those programs to one similar to the box loader function above, you substantially reduce loading time. Several pages of programs could be reduced to the following:
(defun C:PROGM1 () (load "/lsp/progm1") (C:PROGM1)) (defun C:PROGM2 () (load "/lsp/progm2") (C:PROGM2))
(defun C:PROGM3 () (load "/lsp/progm3") (C:PROGM3))
(defun C:PROGM4 () (load "/lsp/progm4") (C:PROGM4))
(defun C:PROGM5 () (load "/lsp/progm5") (C:PROGM5))
(defun C:PROGM6 () (load "/lsp/progm6") (C:PROGM6))
(defun C:PROGM7 () (load "/lsp/progm7") (C:PROGM7))
If you imagine that each of the functions being called from the above example is several lines long then you can see that as the list of programs in Acad.lsp grows, the more space you will save. By setting up your Acad.lsp file in this way, you also save memory since functions are loaded only as they are called.
Using AutoLISP in a Menu
There are two reasons why you might write AutoLISP code directly into the menu file. The first is to selectively load external AutoLISP programs as they are needed. You may have several useful but infrequently used programs that take up valuable memory. You might prefer not load these programs at startup time. By placing them in the menu file, they will only load when they are selected from the menu. In fact, this is what the AutoShade and 3dobjects menu options do. When you pick Ashade from either the screen or pull down menu, and AutoShade is present on your computer, an AutoLISP program called Ashade.lsp is loaded.
The code of the program can be present in the menu file or you can use a method similar to the one described earlier to load external AutoLISP files. However, if you use the menu system to load external AutoLISP files, you must a slightly different method.
In the example we gave for loading programs from external AutoLISP file, the loader program is replaced by the fully operational program of the same name. But if you were to place the following expression in a menu, the program would load every time the menu option was selected.
[box]^C^C(load "box");box
There is nothing wrong with loading the program each time it is run but if the AutoLISP file is lengthy, you may get tired of waiting for the loading to complete every time you select the item from the menu. A better way to load a program from the menu is to use the If function as in the following:
[box]^C^C(if (not C:box)(load "box")(princ "Box is already loaded. ");box
In this example, we show three new functions, If, Not and Princ. The If functions checks to see if certain conditions can be met then evaluates an expression depending on the result. The If functions expects the first argument to test the condition that is to be met while the second argument is the expression to be evaluated if the condition is true. A third expression can optionally be added for cases where you want an expression to be evaluated when the test condition returns nil. The Not function returns a T for true if its argument evaluates to nil, otherwise it returns nil (see figure 2.11).
So, in the menu sample above, if C:BOX does not exists, Not will return T for true and the If function evaluates the (load "box") expression thereby loading the Box program. If C:BOX has already been loaded, then Not function returns nil and box.lsp will not be loaded again. Instead, the second argument will be evaluated. This expression:
(princ "Box is already loaded. ")
simply displays the string:
Box is already loaded.
on the command prompt.
You may have noticed that the If function does not conform to the standard rules of evaluation. Where If is used, the second or third argument is evaluated depending on the value of the first argument.
The second reason for placing code in the menu is speed. Instead of creating a function using Defun, you can set up a program to be read and executed line by line. This saves time since the interpreter reads and executes each expression of your program as they occur in the menu listing instead of reading the entire set of expressions then executing the program. Memory is also saved since Defun is not used to define a new function. Figure 2.11 shows a listing of how the box program from chapter 1 would look as a menu option.
[BOX] ^C^C(setvar "menuecho" 1);+ (setq pt1 (getpoint "Pick first corner: "));\+ (setq pt3 (getcorner pt1 "Pick opposite corner: "));\+ (setq pt2 (list (car pt3) (cadr pt1)));+ (setq pt4 (list (car pt1) (cadr pt3)));+ line; pt1; pt2; pt3; pt4;C;)
NOTE that the Defun function is absent from the listing. When the box option is picked from the menu, AutoCAD reads the associated text just as it would any menu option. Since the text in this case is AutoLISP code, the AutoLISP interpreter evaluates each expression as if it were entered through the keyboard.
NOTE that the semicolon is used to indicate the enter key at the end of each expression. A backslash is used to pause for input, just as you would have a backslash in other commands that require mouse or keyboard input. Also note the use of the plus sign indicating the continuation of the menu item. Finally, note that the last line of the menu item uses the exclamation point to enter the values of the variables as responses to the Line command. The last C is the Close option of the Line command. You may have noticed a new expression:
(setvar "menuecho" 1)
The Setvar function in the above expression does the same thing as the Setvar command in AutoCAD. In this case, it sets the menuecho system variable to 1. This setting prevents the actual AutoLISP code from being displayed on the command prompt.
1. Using your word processor, copy the above listing into a file called Box.mnu, again being careful to input the listing exactly as shown above.
2. Get back into the AutoCAD drawing editor then use the Menu command to load the Box menu you just created. The AutoCAD menu will disappear and will be replaced by the single word Box.
3. Pick the Box option from the menu, and you will see the prompts you entered when you created the Box menu above. This program will work in the same way as the Box.lsp program.
4. To get the AutoCAD menu back, enter the command Menu and enter acad at the menu name prompt.
Since Defun is not used in this example, no argument list is used. Any variables used in the listing becomes global. For this reason, when using menus for AutoLISP programs, it especially important to keep track of variable names so they do not conflict with other variables from other programs.
AutoCAD has a useful feature that allows you to write a set of pre-defines sequence of command and responses stored as an external text file. This scripting ability is similar to writing a macro in the menu file. The main difference between menu macros and scripts is that scripts do not allow you to pause for input. Once a script is issued, it runs until it is completed. Also, unlike menu macros, scripts can exist as independent files that are called as they are needed from the command prompt.
Although it is not commonly done, you can put your AutoLISP programs in a script file. In fact, you can directly convert your AutoLISP program files directly to a script file simply by changing the file extension from .LSP to .SCR. To load your programs, you would use the AutoCAD Script command instead of the AutoLISP load function. There may be times when you want to embed AutoLISP code into a script you have written. You can either write the code directly into the script file or use the load function to load an AutoLISP file from the script.
You have been introduced to some new ways of getting more usefulness from AutoLISP.
- The function Defun allows you to create programs that can be invoked from the AutoCAD command line.
- Variables can be used to pass information between expressions and functions.
- Functions can be created that can be used to respond to AutoCAD commands.
- Functions and programs can be stored as files on disk to be easily retrieved at a later date.
- Frequently used functions can be stored in a file called Acad.lsp to be loaded automatically.
We encourage you to try creating some simple functions and save them to disk. You can start out with the functions presented in this chapter or you can try your hand at writing some functions on your own. You may also want to try using the menu file to store programs or you may want to keep each of your programs as separate files and load them as they are needed.
In the next chapter, you will be introduced to some of the ways you can plan, organize and execute a programming project.