Details, Details, Details...31 Dec, 1998 By: Bill Kramer
Code from all the listings in this article are located in detail.lsp.
When producing a plan, whether it is for a part to be manufactured, a site layout or a highway bridge, there are a lot of details. Many times the details are too small to show up relative to the surroundings in the print. In those cases, a detail enlargement is called for. Making a detail enlargement inside AutoCAD involves copying the objects to a new location and scaling them up so that they are readable on the plot. This operation is not difficult, but it can be tedious and it is the perfect type of application for AutoLISP to tackle. In this month's column we are going to explore what it takes to develop such a useful utility. At the same time we will look at an interesting programming style that is available in the AutoLISP language.
I've often said that the merging of clever algorithms with AutoCAD commands can yield some powerful tools for AutoCAD operators. The detail macro presented this month is no exception. In it we combine AutoCAD commands such as COPY, SCALE and TRIM with AutoLISP control to yield a virtually automatic, scaled detail generator. There are several ways in which details can be rendered in a drawing. We chose to show a circle or bubble in the area to be enlarged with a corresponding bubble for the detail. Figure 1 shows a sample detail enlargement created with this command. A line connects the original bubble to the detail bubble.
In order to write this program we first considered the specific steps that are required from an operator's perspective. Since AutoCAD provides powerful entity object edit commands such as COPY, SCALE and TRIM, we want to make use of them. As an AutoCAD operator, when you want to create a detail enlargement of an area on a drawing, the first step is to select the entity objects in the area to be detailed using the COPY command. A copy of the objects is placed elsewhere in the drawing, someplace that it can be enlarged to provide a detailed view of the objects. The SCALE command is then used to scale the objects to the new size desired. To complete the type of detail shown in Figure 1, add a circle in the original of a known radius. Another circle of the same radius multiplied by the scale factor is then added to the scaled up detail portion of the drawing. The TRIM command can now be used with the scaled up circle as the trim boundary. The objects that extend outside of the circular area are selected for trimming and after several picks they will all be inside the circle. Text, dimensions and other items brought in from the original when the copy was made are typically deleted so that new annotations can be added inside the detail.
The sequence just described is not something a beginning AutoCAD operator will do with ease. In fact, it may take some time to get it right even for a skilled operator. If every drawing or nearly every drawing produced has a detail enlargement in it then the time spent developing a detail enlarger that matches your specific needs is well spent. Knowing the AutoCAD command sequence is only the first step, the next is to break the process down further. For example, in order for the AutoCAD commands to execute properly there may need to be some specific system variables set, such as working in Model Space, having no object snaps and so forth. The drawing environment will dictate what needs to be done before the detail-enlargement sequence takes place. Once we know what the AutoCAD commands require, the next step will be to consider the actual AutoLISP program code and its requirements.
The AutoLISP Program Code
By breaking down the steps, we can begin to write a main program module that will call each of the steps in order to accomplish our goal. Listing 1 contains the main program. This program contains an interesting structure that can be used when writing applications that can fail at multiple points due to operator input or some other reason. A conditional expression (COND) is started in which each of the test predicates is actually a call to one of our functions that accomplishes a particular task. When the task is accomplished with success, a result of nil is returned thus causing the COND expression to proceed to the next predicate. Should a failure result along the way, the function returns a non-nil answer and this satisfies the predicate test and forces the code associated with the test to run. In this example program, the code associated with the test outputs an error message indicating which module failed.
This is somewhat backwards from the way many people think a function should work. Normally, a function returns a value when everything is OK and nil if not. But in programming one is often forced to look for alternative solutions. In this case, such a gander yields a very elegant program structure that forces a modular approach to the program. Modular programming is a good thing in that it isolates specific, testable steps in a process. By making a module and testing it independently you can trust it when the need to use it arises.
The first module in our program example is in Listing 2. This module sets up the AutoCAD environment in a manner that is suitable for creating the detail enlargement. Several system variables are saved in a list to restore back into the system at the end of the program. After saving them in the list SYSVAR_LIST, the system variables are manipulated to fit the application task at hand. Note also that the world coordinate system is established (if not already in place). This is because we will be using the object-selection mechanism for the detail objects. When testing the routines, this setting was added after using a random client drawing that had been saved in an alternative coordinate system. That is one of the benefits of using modular programming concepts, it was a simple matter of returning to that module and inserting the required code.
In Listings 3 and 4, the objects are selected by having the operator identify the center of the detail area and select a circular area to enlarge. In Listing 3, a circle is drawn using the (COMMAND) expression with a PAUSE. The operator is requested to show the radius of the detail area. Immediately after the command is finished, the circle object is accessed and the radius obtained. We could have simply used a (GETDIST) to obtain the radius, but the feel of showing the detail area would not have been there for the operator, so this method was selected instead.
As another example of letting AutoCAD do the math while all we have to do is gather the data, this function does not compute the points needed to select the objects contained in the circle. Instead, the circle created in the detail area is erased and a fifteen-sided polygon is inserted in place of the circle. The points that make up the polygon are then obtained and used in the selection-set get function. Listing 4 picks up the polygon (drawn as a lightweight polyline in the database) and builds a list of the points. The function then proceeds to build a selection set using the "CP" or Crossing Polygon boundary option. The second parameter is a list of the points obtained from the polygon. A better-and faster running-programming alternative is to calculate the list of points directly. However, this demonstrates how AutoCAD commands can be used to solve problems using the geometry engine.
Listing 4 then asks the operator to supply the location where the detail will be rendered. Should the operator not select a point, the function returns a non-nil answer that cancels the remainder of the main function operations. Otherwise, the function continues by filtering out entity objects that are not desired in the detail. This approach was determined to be easiest for operators who want to customize this module. Another and actually much better alternative is to use a filter in the selection set. This set of code was inserted later in the testing of the original program, after it was determined that dimension objects-along with some other object types-did not need to be copied into the detail. It was also decided that this list might be something that should be dynamic, so that an application programmer can adjust it as needed. Coding style is more than just making it fast, one must think about the future life cycle of the code when developing utilities such as these.
Listing 4 next redraws the circles showing the original detail boundary and the new detail area. The entities in the selection set are copied to the new location and a new selection set built consisting of the new entity objects just created. The COPY command creates new entity objects from the set to be copied. To build the selection set, the entity name circle that was drawn for the boundary was preserved before the copy operation took place. Using (ENTNEXT) the entity objects that follow the circle in the drawing database are accessed and placed in the selection set. These objects, along with the circle encompassing the detail copy area are scaled using the scale factor supplied by the operator. A scale factor of 2 is the default.
At this point in the program, the drawing has a detail area that has objects protruding from the boundary circle. Listing 5 contains a program module that trims the excess geometry from the circle. The AutoCAD TRIM command is used to accomplish this feat. Just as in the manual method, we are going to use the circle as the boundary and then present the entity-object selections one at a time to trim back to the circle. Sounds pretty simple, but this is where the programming actually gets a little complicated.
The problem is that when the TRIM command is utilized, it wants not only the entity name of the object to trim, but also the location on the object where it was selected. Using the location, the TRIM command can determine which side of the trim object (group) you want the selected object trimmed to. In other words, if a line is drawn through the circle you must select the line outside of the circle, otherwise the TRIM command removes the object inside the circle.
Although Listing 5 is short, it contains a call to two other functions that are found in Listings 6 and 7. The function in Listing 6 performs the task of finding a point on an object that is outside of the circle boundaries. Of all the functions presented, this one is the longest and it is not complete because more objects can be added as needed based on the type of drawing work. Only a handful of the most popular objects are shown here. The basic objects such as lines, arcs, polylines, splines and circles are supplied, which should satisfy most drawing applications involving detail enlargements. When studying the function in Listing 6, look at each object type individually starting with the simplest ones. It should then be obvious to AutoLISP programmers how to add new objects as needed by your application.
The main advantage of allowing AutoCAD to solve the trim problem is partly demonstrated in the coding requirement for selecting the proper point. Imagine writing the code to trim any entity to any entity! Since that is already inside AutoCAD, there is no reason not to use it for our own applications.
Looping through Selections
Getting back to Listing 5, the function loops through the selection set SS1 taking one entity out at a time. The entity is checked to make sure it is not the circle (TRIM boundary) object using a NOT-EQUAL test. The function in Listing 6 is then called to obtain a point that is on the object, and that point is tested to see if it is outside of the radius. If so, the object is trimmed.
After the trim operation, the function found in Listing 7 is called to check on loop ending conditions. This function tests to see if the selection set is empty. If empty, the number of objects changed in the loop thus far is tested to see if it is greater than zero. This means that the selection set yielded objects that could be trimmed. The operator is then given the chance to request that another iteration of the loop take place to try and clean up any objects that were not completely trimmed. Using our simple trim algorithm, a line that goes all the way through the circle will not get trimmed on both sides during a single pass.
During the development and testing of this function set, there were some geometry situations that did not trim properly and would cause the program to get stuck in an infinite loop. Thus, the operator control was inserted to allow for the final call to be made by the operator as to when the trimming was complete. Note that if there were no trims done during the previous loop iteration, the operator is not given the chance to loop again and the programs sets up to exit the loop.
When the program is instructed to perform another loop, another selection set must be created. Starting with the trim boundary object and then adding all entity objects that follow sequentially in the database creates a selection set using the same technique as before. The loop in Listing 5 continues so long as it can obtain an entity name from the selection set. So, rebuilding the selection set has the end result of causing the main WHILE loop to just keep on going and going.
When the WHILE loop in Listing 5 is finished, the function control is returned to the main program that in turn calls the function in Listing 8. This function adds the final touches to the detail by drawing the line that connects the bubbles and adding a text note about the magnification of the detail enlargement. The magnification text contains a real number that is joined to the text note using a string concatenation. The RTOS subr converts the real number into a string using a variable precision. That precision is determined using the function found in Listing 9 from the toolbox. This function is very handy when developing routines that generate dimensions involving real-number conversions. The real number is presented along with the minimum and maximum precision that should represent it. The function simply loops through the numbers and compares the result of the conversion to get the right fit; a kind of brute-force approach, but it works.
This month's function set demonstrates several programming styles and how AutoLISP can be used to create powerful commands that operators will use over and over again. This detail method can be modified to suit the needs of different drafting styles by changing the AutoLISP code accordingly. Advanced students of AutoLISP will want to incorporate additional object support in the trim operation so items, such as ellipses and block insertions, can be handled.
Until next time, Happy New Year, and keep on programmin'!