Macros of Mass Manipulation (Harry's Code Class Newsletter)

22 Oct, 2007

Macros of Mass Manipulation

Need to change entity objects throughout a batch of AutoCAD drawings? Write an AutoLISP routine to do the dirty work for you.

This month in Harry's Code Class, we'll explore AutoCAD programming related to manipulating entity objects in a drawing en masse. We'll learn to write AutoLISP macros that read through entire drawings and make changes automatically. This topic, as with most topics for this newsletter, is based on current questions and discussions in Cadalyst's Hot Tip Harry Discussion Forums. Do visit the forums for more in-depth conversations about programming and customizing AutoCAD.

Macros of mass manipulation have existed since the early days of AutoLISP. Once access to the drawing entity information became commonplace, it was simple for everyone to write these extreme power tools.

I have a word of caution for beginners. Macros of mass manipulation can and do change drawings. And once you change drawings in this way, it's very difficult to restore original data. The results of initial testing often will not be what you had in mind; thus, it is important to select test subjects you can quickly restore to the original state for another run. Sometimes your first programming attempts can be interesting in other ways. For example, in the early 1980s, I worked on a drawing conversion program and ran a series of test runs that converted piping diagrams into works of modern art that were actually accepted in an international art competition. Fortunately, they did not win, but I've had fun over the years telling people about this and other odd side effects of programming.

My point: Save extra copies of original drawings before you run macros of mass manipulation!

Basics of Mass Manipulation
So how does a macro of mass manipulation work? The structure is almost always the same; variations usually depend on how you are accessing the information in the drawing. If you only need to manipulate the entity objects in model space, a single loop through model space will do the trick. On the other hand, if you need to also update block definitions, a nested loop -- a loop inside a loop -- will be in order. That may sound complicated. It really isn't, once you live through your first one.

The first challenge here is to select the building tool for the macro. VBA (Visual Basic for Applications) and Visual LISP are both well suited for the task. VBA has the advantage of being able to work with multiple drawings more easily than Visual LISP can, but we won't be discussing VBA in this article. Visual LISP and AutoLISP provide all the tools needed here, and there is a very good chance you can find further LISP examples that will get the job done. VBA examples are still somewhat lacking in that regard. (For more information, see last month's Harry's Code Class newsletter feature: VBA vs. Visual LISP -- The Debate Continues.)

Understanding how the drawing structure is presented makes macros of mass manipulation easier to program. When you have a drawing open, you have access to the details of the drawing. The upper level of the drawing holds many of the system variables and settings, but more important to our interests are the tables of data detailing all the entity and reference objects in a drawing. Reference objects include block definitions, layer data, linetype specifics, and so forth. Entity data is actually stored in the block definitions, with every drawing containing the so-called blocks for model space and paper space. Block definitions contain all the entity details in the sequence that they were defined to the drawing.

Whew! Seem like a lot of information? Well, don't worry, because that is what loops do for the programmer. They let us stream through groups of similar data, processing one at a time.

Keys to Success
For LISP programs, the functions (entnext) and (tblnext) are two keys to success.

The (entnext) function allows us to walk through a drawing, one entity at a time. When presented with an entity name as an optional argument, (entnext) returns the name of the entity that follows in the sequence. Nil is returned at the end of the sequence, indicating that your program has reached the finish line. To get the first entity in a drawing, use (entnext) with no arguments. This results in the entity name of the first object drawn. Note: For beginners, Wikipedia offers a good explanation of the programming term argument under the Wiki entry for parameter.

The (tblnext) function works in a similar fashion. It needs to know which table you are interested in, then an optional flag argument is provided to indicate that you want to start at the beginning. For example, the expression (tblnext "LAYER" T) will return the first entry in the layer table. (tblnext "BLOCK" T) will return the first entry in the block definition table. To get the next definition from the table, drop the T and the (tblnext) function will keep track of where you read last.

So, let's say that I wanted to read through the layers table and build a list of all the layers along with the color and line type assignments. The following code will accomplish this task:

  (setq LY (tblnext "LAYER" T) ;;get first layer
        LYRS nil
  ;; Read layer table assembling LYRS list.
  (while LY 
    (setq LYRS 
              (cdr (assoc 2 LY)) ;;Layer name
	  		(cdr (assoc 62 LY));;Color number
		  	(cdr (assoc 6 LY)));;Linetype name
          LY (tblnext "LAYER")))

The program starts by obtaining the first entry in the layers list storing it into LY. The symbol LYRS will be the resulting list, and it is initialized to nil. A while loop is started to get through the list. It will repeat as long as there is a value in LY. Inside the while tool are two variable assignments. The first creates a data list containing the layer name, color number, and line type name that is added to LYRS. The second obtains the next entry in the layer table. When the last layer has been processed the (tblnext) function will return nil, resulting the termination of the while loop.

Loops are powerful workhorses in the programming world. In just a few lines of code, you can process a tremendous amount of data. For example, look how easy it is to loop through all the entities in a drawing in the following listing:

  (setq EN (entnext)) ;;get first object in model space
  (while EN
    (setq EL (entget EN) ;;Get entity data list
	  LY (cdr (assoc 8 EL)) ;;Layer name
	  LT (assoc 6 EL) ;;line type def, if present
	  CL (assoc 62 EL) ;;color number, if present
    (if (or LT CL) ;;either override present?
	(setq LT (if LT (cdr LT) (caddr (assoc LY LYRS)))
	      CL (if CL (cdr CL) (cadr (assoc LY LYRS)))
	      LY (match_layer_attribs LYRS LT CL) ;;custom function!
	      EL (subst (cons 8 LY) (assoc 8 EL) EL)
	(if (assoc 6 EL) (setq EL (vl-remove (assoc 6 EL) EL)))
	(if (assoc 62 EL) (setq EL (vl-remove (assoc 62 EL) EL)))
	(entmod EL)
    (setq EN (entnext EN))

This program portion starts by setting EN to the entity name of the first entity in the drawing. A while loop is then started that will iterate as long as the symbol EN has a value. Given the entity name in EN, the entity data is obtained, from which this program pulls out the layer name, line type name, and color number. The purpose of this code section is to search through the drawing locating all entities that have override linetype or color definitions. If found, the layer data list created in the first listing is searched for a match of the same attributes and the entity layer reference changed accordingly. (A full copy of this application LSP source can be downloaded from one of the links below for your further study.)

Go Easy
As you can see, manipulating entities on a mass level is quite easy. If you ever face a situation where you need to convert a library of drawings or have contractors who submit drawings based on different standards, this is exactly the type of tool you need to build for yourself. Again, remember to take care with your original data in case you make a mistake. Enable several defenses against real damage by making complete backups before starting a test series, not saving the results until you've tested all the components of the mass manipulation, and directing data to a temporary or testing folder instead of back to a network of live drawings. These may sound like common sense -- and quite honestly, they are. However, I am sad to report that I've seen data lost because of these mistakes, so please heed my advice.

To achieve the ultimate macro of mass manipulation you need to do one thing: Keep on programmin'!

Related Resources

For more information, the following links contain examples and discussions related to building macros of mass manipulation.