AfraLISP - Learn AutoLISP for AutoCAD productivity

Into the Database

by Kenny Ramage

Into the Database - Part 1

Hold onto your hat because we're going to dive straight in here.
Fire up AutoCad and draw a line anywhere on the screen.
Now type this then press Enter:

	(setq a (entlast))

Lisp should return something like this:

	<Entity name: 2680880>

This is the Entity Name of the Line that you have just drawn.
Now type "Erase" then !a and press Enter twice.
The line should disappear. Type "OOPS" to bring it back.
You have just modified the AutoCAD Database.

Now type this :

	(setq b (entget a))

This will retrieve the Entity Data. It should look something like this:

((-1 . <Entity name: 2680880>) (0 . "LINE") (5 . "270")
(100 . "AcDbEntity") (67 . 0) (8 . "0") (100 . "AcDbLine") 
(10 400.378 621.82 0.0) (11 740.737 439.601 0.0) 
(210 0.0 0.0 1.0))

Now type this line:

	(setq c (cdr (assoc 10 b)))

Lisp should return:

	(400.378 621.82 0.0)

Before you type the next few lines, make sure that your snap is turned off.

Command: circle
3P/2P/TTR/<Center point>: !c
Diameter/<Radius> <10.0>: 20

A circle should be drawn at the end of the line.

This exercise was just to show you the ability of AutoLisp to go behind your drawing, into the database, and manage graphic and non-graphic information. This tutorial will show you how entities are stored and referenced in the database.
It will show you how AutoLisp reveals data about entities and how they can be modified and manipulated. Say au revoir to your wife and kid's and let's visit the AutoCad Database. (Star Trek theme music now starts…)

Into the Database - Part 2

When you first start to delve into the AutoCAD database it is, I admit, quite daunting. But, although entity access and manipulation is fairly complex, it can be divided into component parts that make it much easier to understand.
Let's have a look at an AutoLisp routine that can be used, as a sort of template
which you can apply to numerous, similar applications. Have a close look at this coding :

(defun C:CHLAYER ( / a1 a2 n index b1 b2 b3 d1 d2)
		
	(prompt "\nSelect Entities to be Changed : ")
	(setq a1 (ssget))
	(prompt "\nPoint to Entity on Target Layer : ")
	(setq a2 (entsel))
	(setq n (sslength a1))
	(setq index 0)
	(setq b2 (entget (car a2)))
	(setq d2 (assoc 8 b2))
	(repeat n
		(setq b1 (entget (ssname a1 index)))
		(setq d1 (assoc 8 b1))
		(setq b3 (subst d2 d1 b1))
		(entmod b3)
		(setq index (1+ index))
	);repeat
   (princ)
);defun
(princ)

This routine allows you to select any number of objects and change them to a different layer. The target layer is choosen by simply pointing to an object on the desired layer. (To test this routine, you will need to create a drawing with objects on different layers.) Let's have a look line by line :

(defun C:CHLAYER ( / a1 a2 n index b1 b2 b3 d1 d2)

Defines the function and declares all variables as local.

	(prompt "\nSelect Entities to be Changed : ")

Prompts the user.

	(setq a1 (ssget))

Allows the user to select the objects to be changed. The selection set is assigned to variable 'a1'.

	(prompt "\nPoint to Entity on Target Layer : ")

Prompts the user to select the Target Layer.

	(setq a2 (entsel))

This is a special type of selection statement that only allows you to
select one entity.

	(setq n (sslength a1))

Counts the number of entities in the selection set 'a1' and stores this number in variable 'n'.

	(setq index 0)

Sets the loop control variable 'index' to zero.

	(setq b2 (entget (car a2)))

This statement retrieves the entity list from 'a2' and assigns it to 'b2'.

	(setq d2 (assoc 8 b2))

This looks for the code 8 in the entity list 'a2', and then assigns the sub list to 'd2'.

	(repeat n

This begins the loop that pages through the selection set.

		(setq b1 (entget (ssname a1 index)))

This gets the entity list and assigns it to 'b1'.

		(setq d1 (assoc 8 b1))

Gets the sublist code 8. (The Layer)

		(setq b3 (subst d2 d1 b1))

Substitutes the new 'd2' layer for the old 'd1' layer in the entity list 'a1', and assigns it to the new entity list 'b3'.

		(entmod b3)

Updates the new entity list in the database.

		(setq index (1+ index))

Increases the 'index' variable by 1, priming it for the next loop.

	);repeat

Closes the repeat loop.

   (princ)

Finish cleanly.

);defun

Closes the function.

(princ)

Clean Loading.

Listed below is another routine that allows you to globally change the height of text without affecting other entities. As you will see, the only difference is, is that we have added a conditional filter to the routine.

(defun C:CHGTEXT ( / a ts n index b1 b2 b c d)
	
	(setq a (ssget))
	(setq ts (getreal "\nEnter New Text Height : "))
	(setq n (sslength a))
	(setq index 0)
	(repeat n
		(setq b1 (entget (ssname a index)))
		(setq index (index+ 1))
		(setq b (assoc 0 b1))
		(if (= "TEXT" (cdr b))
			(progn
				(setq c (assoc 40 b1))
				(setq d (cons (car c) ts))
				(setq b2 (subst d c b1))
				(entmod b2)
			);progn
		);if
	);repeat
   (princ)
);defun
(princ)

Well I bet your brain hurts after that lot!!!
On the text page we'll have a quick look at Tables.

Into the Database - Part 3

The AutoCad Database does not only consist of entities but also includes several other sections, such as the Tables Section.
Tables store information about entities that are maintained globally within the drawing. For example, when you insert a block into a drawing, how does AutoCAD know what the block looks like? The definition of a block is stored in the Block Table. What happens, for example, if you need to create a layer? You have to know if the layer already exist because if you try to create a layer that already exists your program will crash. Therefore, you would search the Layers Table first to see if the layer exists.

There are nine (9) Tables that you can access :

	Layer Table				"LAYER"
	Linetype Table				"LTYPE"
	Named View Table			"VIEW"
	Text Style Table			"STYLE"
	Block Table				"BLOCK"
	Named UCS Table				"UCS"
	Named Application ID Table		"APPID"
	Named Dimension Style Table		"DIMSTYLE"
	Vport Configuration Table		"VPORT"

A Table is split into 2 parts: The 'names' of the entries in the Table and the 'details' of each entry. For example, in the Layers Table, the name of the entries would be the names of the layers that exist. The details of an individual layer would be colour, linetype, on, off, frozen, thawed, locked, unlocked or current.

To access a Table we would use the (tblsearch) function. Let's have a look at an example :

Assume that you want to know whether a layer called STEEL exists in your drawing. First create the layer STEEL then type the following :

	(setq t (tblsearch "LAYER" "STEEL"))

The entity list of the layer STEEL should be returned :

((0 . "LAYER") (2 . "STEEL") (70 . 64) (62 . 7) (6 . "CONTINOUS"))

The first part of the entity list is '0', indicating Associative 0.
In this case it's an entry in the "LAYER" Table.
Associative 2 indicates the name of the layer. STEEL in our case.
Associative 70 is the state of the entity. 1 is Frozen, 2 is Frozen on new paper space view ports and 4 is locked. These numbers are added to 64. In this case the layer is neither frozen nor locked.
Associative 62 is the colour of the layer. Ours is white which is colour number 7. If the colour number is a negative number then the layer is off.
Associative 6 is the linetype of the layer, in this case, "CONTINUOUS".

If the (tblsearch) had not found the layer then it would have returned 'nil' and you would know that the layer did not exist.

Sometimes you don't know the name of a layer or a block but you need a list of them. This is when the (tblnext) function comes into play.

Let's assume that 4 layers exist in your drawing. The layer names are MECH, STEEL, PIPE and TXT. Enter the following :

(tblnext "Layer")

AutoLISP should return something like this :

((0 . "LAYER") (2 . "PIPE") (70 . 0) (62 . 7) (6 . "CONTINUOUS"))

Repeat the same command four additional times. You will get a new entity list for each of the layers. The last time you type the command AutoLISP will return 'nil' because there are no more additional layers.

Let's have a look at this in action. We are going to design a dialogue box that displays a drop down list of all the available layers in a specific drawing. The user can then choose any layer which can then be used within his routine.

Get Layer dialogue box

The Dialogue Coding looks like this :

getlay  : dialog {				//dialog name
 
        label = "Get Layer" ;			//give it a label
 
 
	: popup_list {				//define list box
 
        key = "sel1";				//give it a name
 
	value = "0";				//initial value
 
        }					//end list
 
 
        ok_cancel ;				//predifined OK/Cancel
 
     					
}						//end dialog
 

Now the AutoLisp Coding :

(defun c:getlay ( / NAMES SIZ1)
;define funcion
 
	(setvar "cmdecho" 0)
	;switch off command echo
 
 	(setq siz1 "0")
	;initiliase variable
 
	(setq userclick T)
	;set flag
 
	(setq f 1)
	;rewind pointer
 
	(while
	;start while loop
 
 	  (setq t1 (tblnext "Layer" f))
	  ;get the next layer
 
	  (setq f nil)
	  ;reset pointer
 
	  (setq b (cdr (assoc 2 t1)))
	  ;get the layer name
 
	    (if (/= b "DEFPOINTS")
	    ;if name is not equal to DEFPOINTS
 
	       (setq NAMES (append NAMES (list b)))
	       ;Add the layer name to the list
 
	    );if
 
	);while
 
 
	(setq dcl_id (load_dialog "getlay.dcl"))
	;load dialogue
 
	(if (not (new_dialog "getlay" dcl_id)
	;check for errors
 
	      );not
 
	     (exit)
	     ;if problem exit
 
	);if
  
  (set_tile "sel1" "0")
  ;initilise list box
 
  (start_list "sel1")
  ;start the list
 
  (mapcar 'add_list NAMES)
  ;add the layer names
 
  (end_list)
  ;end the list
 
    (action_tile
 
    "cancel"	
 
    "(done_dialog) (setq userclick nil)"
 
    );action_tile
    ;if cancel set flag to nil
 
  (action_tile
 
    "accept"	
 
    (strcat	
 
      "(progn 
 
        (setq SIZ1 (get_tile \"sel1\"))"	
 
       " (done_dialog)(setq userclick T))"	
 
    );strcat
 
  );action tile
  ;if OK get the layer selected
 
  (start_dialog)	
 
  (unload_dialog dcl_id)	
 
   (if userclick	
   ;if flag true
 
    (progn
 
     (setq SIZ1 (nth (atoi SIZ1) NAMES))
     ;get the name of the layer from the list
 
     (alert (strcat "\nYou Selected Layer  " SIZ1))
     ;display the name
 
    );end progn
 
   );end if
 
(princ)
 
);defun C:getlay
 
(princ)

If you would like the source coding for this AutoLisp Routine then simply Click Here. Cheers and keep well…