AfraLISP - Learn AutoLISP for AutoCAD productivity

Polylines and Blocks

by Kenny Ramage

Polylines and Blocks! Come back… Don't run away…

Honestly, they are a lot easier to deal with than you think. I know that they are called "complex entities", but the only difference between them and other entities is that we just have to dig a bit deeper to get to what we want. In fact, once we get there I'll show you a couple of things that you swear is magic. So bear with me, take your time, and hang on for a ride on the magic carpet…

Polylines

The LwPolyline (Light Weight Polyline) entity, or "optimized polyline," was introduced with Release 14. A lwpolyline is defined in the drawing database as a single graphic entity. This is different than a standard polyline, which is defined as a group of subentities. Lwpolylines display faster and consume less disk space and RAM.

In Release 14 and above, 3D polylines are always created as standard polyline entities. 2D polylines are created as lwpolyline entities by default unless they have been curved or fitted with the PEDIT command. When a drawing from a previous release is opened in Release 14 and above, all 2D polylines convert to lwpolylines automatically unless they have been curved or fitted or contain xdata.

We will have a look at the R13 and below Polyline first. To start, draw 3 joined polylines. (3DPoly if you are using R14 and above). Then type this :

	Command: (setq e (entget (car (entsel))))

AutoLisp should return something like this :

Select object: ((-1 . <Entity name: 26508d8>) (0 . "POLYLINE") (5 . "27B")
  (100 . "AcDbEntity") (67 . 0) (8 . "0") (100 . "AcDb3dPolyline") (66 . 1)
  (10 0.0 0.0 0.0) (70 . 8) (40 . 0.0) (41 . 0.0) (210 0.0 0.0 1.0) (71 . 0)
  (72 . 0) (73 . 0) (74 . 0) (75 . 0))

Hey, wait a minute!… AutoLisp has returned the entity list, and I can see that it's a Polyline, but there are no co-ordinates, and where does AutoLisp get the co-ordinates for all the vertices?

As I said earlier, we need to dig a little bit deeper to get the information we require. This is where the (entnext) function comes into play.

Now type the following 5 code segments :

Command: (setq e1 (entget (entnext (cdr (car e)))))
 ((-1 . <Entity name: 26508e0>) (0 . "VERTEX") (5 . "27C") (100 . "AcDbEntity") 
  (67 . 0) (8 . "0") (100 . "AcDbVertex") (100 . "AcDb3dPolylineVertex")
  (10 391.774 521.633 0.0) (40 . 0.0) (41 . 0.0) (42 . 0.0) (70 . 32)
  (50 . 0.0) (71 . 0) (72 . 0) (73 . 0) (74 . 0))
 
Command: (setq e2 (entget (entnext (cdr (car e1)))))
 ((-1 . <Entity name: 26508e8>) (0 . "VERTEX") (5 . "27D") (100 . "AcDbEntity") 
  (67 . 0) (8 . "0") (100 . "AcDbVertex") (100 . "AcDb3dPolylineVertex")
  (10 758.971 383.418 0.0) (40 . 0.0) (41 . 0.0) (42 . 0.0) (70 . 32)
  (50 . 0.0) (71 . 0) (72 . 0) (73 . 0) (74 . 0))
 
Command: (setq e3 (entget (entnext (cdr (car e2)))))
 ((-1 . <Entity name: 26508f0>) (0 . "VERTEX") (5 . "27E") (100 . "AcDbEntity") 
  (67 . 0) (8 . "0") (100 . "AcDbVertex") (100 . "AcDb3dPolylineVertex")
  (10 257.549 377.344 0.0) (40 . 0.0) (41 . 0.0) (42 . 0.0) (70 . 32)
  (50 . 0.0) (71 . 0) (72 . 0) (73 . 0) (74 . 0))
 
Command: (setq e4 (entget (entnext (cdr (car e3)))))
 ((-1 . <Entity name: 26508f8>) (0 . "VERTEX") (5 . "27F") (100 . "AcDbEntity") 
  (67 . 0) (8 . "0") (100 . "AcDbVertex") (100 . "AcDb3dPolylineVertex")
  (10 391.774 521.633 0.0) (40 . 0.0) (41 . 0.0) (42 . 0.0) (70 . 32)
  (50 . 0.0) (71 . 0) (72 . 0) (73 . 0) (74 . 0))
 
Command: (setq e5 (entget (entnext (cdr (car e4)))))
 ((-1 . <Entity name: 2650900>) (0 . "SEQEND") (5 . "280") (100 . "AcDbEntity") 
  (67 . 0) (8 . "0") (-2 . <Entity name: 26508d8>))

The (cdr (car e)) returns the entity name of entity list e. Each code segment then uses the (entnext entity name) of the entity that precedes it.

Take note of the entity type of each variable :

e	(0 . "POLYLINE")
e1	(0 . "VERTEX")
e2	(0 . "VERTEX")
e3	(0 . "VERTEX")
e4	(0 . "VERTEX")
e5	(0 . "SEQEND")

Do you see that a 3 line Polyline consists of a master or, parent list, 4 vertex and an end-of sequence ("SEQEND") list.

To extract the entitity list for each vertex is therefore, quite easy. We just need to loop through the sequence of vertices until we reach the SEQEND list.

Here's an example of a function that will print the coordinates for each vertex of a Polyline :

(defun c:coord ( / e r)
 
	(setq e (entget (car (entsel))))
	;get the parent entity list
 
	(setq r 1)
	;set loop control number to 1
 
	(while r
	;while loop control is not nil, carry on looping
 
	   (setq e (entget (entnext (cdr (car e)))))
	   ;get the vertex entity list
 
	   (if (/= (cdr (assoc 0 e)) "SEQEND")
	   ;if it is not "end-of-sequence
 
		(progn
		;do the following
 
		  (terpri)
		  ;new line
 
		  (princ (cdr (assoc 10 e)))
		  ;print the co-ordinates
		  
		);progn
 
	        (setq r nil)
		;if end of sequence, stop looping
 
	   );if
 
  	);while
 
  (princ)
 
);defun
 
(princ)

There is a quicker way of retrieving the entity list of a Polyline vertex. The function (nentsel) let's you select an entity and returns the name of the entity even if it belongs to a polyline. Try it for yourself. Type this, then select any vertex of a polyline.

	(setq e (entget (car (nentsel))))

AutoLisp should return something like this :

Select object: ((-1 . <Entity name: 2100880>) (0 . "POLYLINE") (5 . "270")
  (100 . "AcDbEntity") (67 . 0) (8 . "0") (100 . "AcDb3dPolyline") (66 . 1)
  (10 0.0 0.0 0.0) (70 . 9) (40 . 0.0) (41 . 0.0) (210 0.0 0.0 1.0) (71 . 0)
  (72 . 0) (73 . 0) (74 . 0) (75 . 0))

How's that? Straight to the entity list!

Now, while we're here, let's have a quick look at blocks…

Blocks

Create a block consisting of a couple of lines and a circle with a radius of 20. Now type this :

	(setq e (entget (car (nentsel))))

Now pick the circle. AutoLisp should return something like this:

Select object: ((-1 . <Entity name: 2100910>) (0 . "CIRCLE") (5 . "282")
  (100 . "AcDbEntity") (67 . 0) (8 . "0") (100 . "AcDbCircle") (10 0.0 -1.51849 0.0)
  (40 . 20.0) (210 0.0 0.0 1.0))

Hey, hang about. It's returned the entity list of the circle even though it's part of a block! Now type the following:

	(setq d (assoc 40 e))
	(setq e1 (subst '(40 . 50.0) d e))
	(entmod e1)

Now REGEN the drawing. Did you noticed what happened? The radius of the circle has change even though it's part of a block. Not only that, it has also redefined the block definition. In other words, every block in the drawing with the same name would have changed. I told you it was magic…

Let's change the layer of the circle:

	(setq d (assoc 8 e1))
	(setq e2 (subst '(8 . "STEEL") d e1))
	(entmod e2)

Again, REGEN the drawing. The circle has changed to Layer "STEEL". Now do you see what I mean by magic. I bet you never thought you would be able to modify a block without exploding it. Well you can now…

LwPolylines

As we mentioned earlier, LwPolylines were introduced with AutoCAD Release 14. They differ in that they are defined as a single entity. Let's have a look at a LwPolylines entity list. Draw a LwPolyline and enter this:

	(setq e (entget (car (entsel))))

AutoLisp should return something like this:

Select object: ((-1 . <Entity name: 2100938>) (0 . "LWPOLYLINE") (5 . "287") 
  (100 . "AcDbEntity") (67 . 0) (8 . "0") (100 . "AcDbPolyline") (90 . 3) (70 . 1)
  (43 . 0.0) (38 . 0.0) (39 . 0.0) (10 597.908 661.3) (40 . 0.0) (41 . 0.0) 
  (42 . 0.0) (10 1179.86 476.045) (40 . 0.0) (41 . 0.0) (42 . 0.0) (10 479.39 397.084)
  (40 . 0.0) (41 . 0.0) (42 . 0.0) (210 0.0 0.0 1.0))

As you can see, there is no need to use (entnext) to step through the vertex entities as the group 10 entity code is already part of the parent list. But, we do have another problem!! There are numerous group 10 entity codes. (As well as Group 40 - Start Width; Group 41 - End Width and Group 42 - Bulge) To extract these, we need to first, find the length of the list. Then we must loop through each code entity in the list searching for a group 10. Once found, we can easily extract the vertex co-ordinates. The following function does just that:

(defun c:lwcoord (/ e len n e1)
 
	(setq e (entget (car (entsel))))
	;get the entity list
 
	(setq len (length e))
	;get the length of the list
 
	(setq n 0)
	;set counter to zero
 
	(repeat len
	;repeat for the length of the entity list
 
	  (setq e1 (car (nth n e)))
	  ;get each item in the entity list
	  ;and strip the entity code number
 
	  (if (= e1 10)
	  ;check for code 10 (vertex)
 
	    (progn
	    ;if it's group 10 do the following
 
		(terpri)
		  ;new line
 
		  (princ (cdr (nth n e)))
		  ;print the co-ordinates
		  
	    );progn
 
	  );if
	  (setq n (1+ n))
	  ;increment the counter
 
	);repeat
 
  (princ)
);defun
(princ)

Well, that's it concerning Polylines and Blocks. I told you it was easy! Remember, I've only scratched the surface with the things that you can do once you dig into entity lists, especially with complex entities. One last thing. Want to create your own entity? Normally, in AutoLISP you would draw a line like this:

	(command "Line" pt1 pt2 "")

Now, create you own line by doing this:

(setq e '((0 . "LINE")(8 . "0")(10  50.0 50.0 0.0)(11 100.0 100.0 0.0)))
(entmake e)

Makes you think, doesn't it?

Cheers for now…