AfraLISP - Learn AutoLISP for AutoCAD productivity

Visual LISP and Layers

by Kenny Ramage

I have had so many queries lately regarding Visual Lisp and Layers, that I decided to dedicated a complete section dedicated to this subject. Please remember though, that what you read here is not all encompassing in regards to Layers and could possible be added to over time. (in other words, I'm making excuses in case I miss anything).

Layers in AutoCAD are contained within the Layers Collection which is stored in the Document Object which is part of the Documents Collection which is part of the Application Object or AutoCAD itself. Confused? Have a look at an extract from the AutoCAD Object Model :

Application(Object)
        |
        |
        |------- Documents[Collection]
        |            |
        |            |
        |            |
        |        Document(Object) 
        |            |
        |            |
        |            |------ Layers-------Layer
        |            |    [Collection]   (Object)
        |            |
        |            |
        |            |
        |            |

To retrieve a Layer to play with, we first need to access the Layers Collection.
Type this at the Console prompt :

_$ (vl-load-com)

_$ (setq acadobject (vlax-get-Acad-Object))
#<VLA-OBJECT IAcadApplication 00adc088>

We are now in the Application Object. Now let's sneak into the Document Object :

_$ (setq activedocument (vla-get-activedocument acadobject))
#<VLA-OBJECT IAcadDocument 01945554>

And into the Layers Collection we go!

_$ (setq LayerTable (vla-get-layers activedocument))
#<VLA-OBJECT IAcadLayers 01a070bc>

"But, hang on one second, you jumped from the Application Object straight to the Document Object!!"
Hey, well spotted. I'm glad to see that you're wide awake. OK, I suppose an explanation would be in order.
Let's run a dump on the Application Object :

_$ (vlax-dump-object acadobject)
; IAcadApplication: An instance of the AutoCAD application

; Property values:
; ActiveDocument = #<VLA-OBJECT IAcadDocument 01945504>
; Application (RO) = #<VLA-OBJECT IAcadApplication 00adc088>
; Caption (RO) = "AutoCAD 2000 - [Drawing1.dwg]"
; Documents (RO) = #<VLA-OBJECT IAcadDocuments 02d06860>
; FullName (RO) = "C:\\ACAD2000\\acad.exe"
; Height = 776
; LocaleId (RO) = 1033
; MenuBar (RO) = #<VLA-OBJECT IAcadMenuBar 02d096c4>
; MenuGroups (RO) = #<VLA-OBJECT IAcadMenuGroups 015baf2c>
; Name (RO) = "AutoCAD"
; Path (RO) = "C:\\ACAD2000"
; Preferences (RO) = #<VLA-OBJECT IAcadPreferences 015bb9ec>
; StatusId (RO) = ...Indexed contents not shown...
; VBE (RO) = #<VLA-OBJECT VBE 03404a04>
; Version (RO) = "15.0h (Hardware Lock)"
; Visible = -1
; Width = 1032
; WindowLeft = -4
; WindowState = 3
; WindowTop = -4
T

Do you see what I see? The Active Document Object is a Property of the Application Object. This means that we can access it directly without having to retrieve it from the Documents Collection. Clever hey?

Still not convinced. OK, let's do it the long way around :

_$ (setq acadobject (vlax-get-Acad-Object))
#<VLA-OBJECT IAcadApplication 00adc088>

Again we've accessed the Application Object. Now let's get into the Documents Collection :

_$ (setq documentcollection (vla-get-documents acadobject))
#<VLA-OBJECT IAcadDocuments 01a03d30>

Now, we'll retrieve the Document Object for the drawing

_$ (setq thedocument (vla-item documentcollection 0))
#<VLA-OBJECT IAcadDocument 01a6f6a4>

And finally we retrieve the Layers Collection.

_$ (setq LayerTable (vla-get-layers thedocument))
#<VLA-OBJECT IAcadLayers 02fdca64>

But, what happens if we have two or more drawings open and we want to access the Layers Collection of one of the other drawings?
Let's think about this. Where would the inactive documents (your other drawings) be stored?
I would say in the Documents Collection wouldn't you agree?
Let's try this out. First of all open two new drawings, Drawing1.dwg and Drawing2.dwg. Ensure that Drawing1.dwg is the active drawing. Now type this at the Console prompt.

_$ (setq acadobject (vlax-get-Acad-Object))
#<VLA-OBJECT IAcadApplication 00adc088>

Again we've accessed the Application Object. Now let's get into the Documents Collection :

_$ (setq documentcollection (vla-get-documents acadobject))
#<VLA-OBJECT IAcadDocuments 01a03d30>

Now, we'll retrieve the Document Object for the specific inactive drawing

_$ (setq thedocument (vla-item documentcollection "Drawing2.dwg"))
#<VLA-OBJECT IAcadDocument 01a6f6a4>

And finally we retrieve the Layers Collection.

_$ (setq LayerTable (vla-get-layers thedocument))
#<VLA-OBJECT IAcadLayers 02fdca64>

Let's test this out. Let's change the current Layer in Drawing2.dwg to Layer 0. Ensure that Drawing1.dwg is still the active drawing and enter this at the Console prompt :

_$ (vla-put-activelayer thedocument (vla-item LayerTable 0))
nil

Switch to Drawing2.dwg. Layer 0 should have become the current Layer.


To avoid confusing you any further by dealing with multiple workspaces, let's go back to just one active drawing. Close all drawings and open a new drawing. Enter this at the Console prompt :

_$ (setq acadobject (vlax-get-Acad-Object))
#<VLA-OBJECT IAcadApplication 00adc088>
_$ (setq activedocument (vla-get-activedocument acadobject))
#<VLA-OBJECT IAcadDocument 01945554>
_$ (setq LayerTable (vla-get-layers activedocument))
#<VLA-OBJECT IAcadLayers 01a070bc>

No messing about this time! Straight to the Layers Collection.
Right, down to the nitty gritty. First create a Layer in your drawing named "TestLayer".
OK, now let's access "TestLayer" from the Layers Collection :

_$ (setq theLayer (vla-item LayerTable "TestLayer"))
#<VLA-OBJECT IAcadLayer 02fce56c>

Let's list the properties and methods of this Layer :

_$ (vlax-dump-object theLayer T)
; IAcadLayer: A logical grouping of data, similar to transparent acetate overlays on a drawing

; Property values:
; Application (RO) = #<VLA-OBJECT IAcadApplication 00adc088>
; Color = 7
; Document (RO) = #<VLA-OBJECT IAcadDocument 01945554>
; Freeze = 0
; Handle (RO) = "957"
; HasExtensionDictionary (RO) = 0
; LayerOn = -1
; Linetype = "CONTINUOUS"
; Lineweight = -3
; Lock = 0
; Name = "TestLayer"
; ObjectID (RO) = 26864120
; ObjectName (RO) = "AcDbLayerTableRecord"
; OwnerID (RO) = 26862608
; PlotStyleName = "Color_12"
; Plottable = -1
; ViewportDefault = 0

; Methods supported:
; Delete ()
; GetExtensionDictionary ()
; GetXData (3)
; SetXData (2)
T

To create a new Layer, we simply add it to the Layers Collection :

_$ (setq aNewLayer (vla-add LayerTable "NewLayer"))
#<VLA-OBJECT IAcadLayer 02fce7bc>

You should have a new Layer in your drawing named "NewLayer".
But, our new Layer has been created with default colour and linetype values namely, 7 and Continuous.
Let's change them :

_$ (vla-put-color aNewLayer 2)
nil


Changes the colour of our Layer to 2 (yellow).

_$ (vla-put-linetype aNewLayer "Dashed2")
nil

Changes the linetype of our Layer to "Dashed2". "Dashed 2" of course, must be loaded within our drawing.
Conversely, if you want to find the Colour and Linetype of a particular Layer you would do this :

_$ (vla-get-color aNewLayer)
2

_$ (vla-get-linetype aNewLayer)
"DASHED2"


Let's play around with our new Layer. First, change to any other Layer in your drawing.
OK, let's switch our Layer OFF and then back ON :

_$ (vla-put-layeron aNewLayer :vlax-false)
nil

_$ (vla-put-layeron aNewLayer :vlax-true)
nil

Next we'll FREEZE our Layer :

_$ (vla-put-freeze aNewLayer :vlax-true)
nil

And to THAW the Layer :

(vla-put-freeze aNewLayer :vlax-false)
nil

Right, now we'll LOCK it :

_$ (vla-put-Lock aNewLayer :vlax-true)
nil

And now UNLOCK the Layer :

_$ (vla-put-Lock aNewLayer :vlax-false)
nil

Should we now make the Layer UNPLOTTABLE?

_$ (vla-put-plottable aNewLayer :vlax-false)
nil

Now we'll make the Layer PLOTTABLE :

_$ (vla-put-plottable aNewLayer :vlax-true)
nil

Want to change the Layer's LINEWEIGHT? Let's change it to 0,35mm :

_$ (vla-put-LineWeight aNewLayer 35)
nil

Let's change the Lineweight back to DEFAULT :

_$ (vla-put-LineWeight aNewLayer -3)
nil

"ByLwDefault" = -3
"ByBlock" = -2
"ByLayer" = -1
Other Values are : 0, 5, 9, 13, 15, 18, 20, 25, 30, 35, 40, 50, 53, 60, 70, 80, 90, 100, 106, 120, 140, 158, 200, 211.


In AutoCAD, make our new Layer the current Layer, and draw a line.
Now let's delete our new Layer :

_$ (vla-delete aNewLayer)
; error: Automation Error. Object is referenced by other object(s)

Oh, oh. We have an error. Think about it! How can we delete the Layer if it is being referenced by our Line Object?
OK, Let's delete the Line and try again :

_$ (vla-delete aNewLayer)
; error: Automation Error. Object is referenced by other object(s)

What, still an error? That's because the Layer is current and is therefore referenced by the Document Object.
Now make any other Layer current and try for a third time :

_$ (vla-delete aNewLayer)
nil

Hurray, success at last. The Layer is now an "Ex-Layer".


Here's a couple of Layer routines written using Visual Lisp that you may find useful.

Turn All Layers ON :

(defun C:Layeron ( / acadDocument theLayers) 
(vl-load-com) 
(setq acadDocument (vla-get-activedocument (vlax-get-acad-object))) 
(setq theLayers (vla-get-layers acadDocument)) 
(vlax-for item theLayers (vlax-put-property item "LayerOn" ':vlax-true) ) 
(princ) 
);defun 
(princ)

This routine will place a Prefix in front of all Layer names ;and rename them.
Of course, it will not rename Layer "0" or "Defpoints".

(prompt "\nType ChLayName to run.........")
(defun C:ChLayName ( / acadDocument theLayers layName pre)
(vl-load-com)
(setq pre (getstring "\nEnter Layer Prefix : "))
(setq acadDocument (vla-get-activedocument (vlax-get-acad-object)))
(setq theLayers (vla-get-layers acadDocument))
(vlax-map-collection theLayers 'layer-mod)
(princ)
);defun

(defun layer-mod (theLayer)
  (setq layName (vlax-get-property theLayer 'Name))
  (if (not (member layName '("0" "Defpoints")))
    (vla-put-Name thelayer (strcat pre layName))
  ) ;if
);defun
(princ)


Create a layer using Visual Lisp?

;;;Returns a layer object or nil
;;;on creation failure
(defun mLayer (LayerName)
(vl-load-com)
   (setq LayerName
       (vl-catch-all-apply
           'vla-add
           (list
               (vla-get-layers
                   (vla-get-activedocument
                       (vlax-get-acad-object)
                   )
               )
               Layername
           )
       )
   )
   (if (vl-catch-all-error-p LayerName)
       nil
       LayerName
   )
)


This routine will return a list of all Layers in the active drawing :

(defun C:LayList ( / acadobject activedocument LayerTable thelist)
(vl-load-com)
(setq acadobject (vlax-get-Acad-Object))
(setq activedocument (vla-get-activedocument acadobject))
(setq LayerTable (vla-get-layers activedocument))
(vlax-for each LayerTable
(setq thelist (cons (vla-get-Name each) thelist))
)
(if thelist (reverse thelist))
);defun
(princ)