AfraLISP - Learn AutoLISP for AutoCAD productivity

Arrays with Visual LISP

by Kenny Ramage

If you've programmed in VBA or other languages, you're probably familiar with the concept of arrays. An array is a named collection of variables of the same data type. Each array element can be distinguished from other elements by one or more integer indexes. For example, if the "sheetNames" array contains three names, you can set and return the names in VBA as shown in the following example :

sheetNames(0) = "Sheet1"
sheetNames(1) = "Sheet2"
sheetNames(2) = "Sheet3"

MsgBox sheetNames(1)

Would return a message :


Arrays allow you to group related variables in a way that makes it easier for you to keep track of, access, and manipulate them all at once, while still being able to access each variable individually. This helps you create smaller and simpler routines in many situations, because you can set up loops using index numbers to deal efficiently with any number of cases.

When you create an array, its size is determined by the number of dimensions it has and the by the upper and lower bounds for the index numbers in each dimension. The "sheetNames" array in the earlier example has one dimension and three elements; the lower bound is 0 and the upper bound is 2.

"That's fine Kenny, but how do we create an Array in Visual Lisp?"

OK I hear you, there's no need to yell!
Enter this at the Console prompt :

_$ (vl-load-com)

_$ (setq sheet_type (vlax-make-safearray vlax-vbString '(0 . 2)))
_$ (vlax-safearray-fill sheet_type '("Sheet1" "Sheet2" "Sheet3"))

Have a look at the "sheet_ type" variable in the Watch window :

Inspect: safearray

This tells us that the Array contains strings, has one dimension and contains 3 elements. Let's convert it to an AutoLisp List :

_$ (setq alist (vlax-safearray->list sheet_type))
("Sheet1" "Sheet2" "Sheet3")

Did a light just go off in your head? An Array is just a List in a slightly different format. To create an Array, or safearray as they are know in Visual Lisp, we use the "vlax-make-safearray" function. To populate a safearray we use the "vlax-safearray-fill" or the "vlax-safearray-put" functions.

The "vlax-make-safearray" function requires a minimum of two arguments. The first argument identifies the type of data that will be stored in the array.
One of the following constants must be specified for the data type :

vlax-vbInteger Integer
vlax-vbLong Long Integer
vlax-vbSingle Single-precision floating point number
vlax-vbDouble Double-precision floating point number
vlax-vbString String
vlax-vbObject Object
vlax-vbBoolean Boolean
vlax-vbVariant Variant

The remaining arguments to "vlax-make-safearray" specify the upper and lower bounds of each dimension of the array. The lower bound for an index can be zero or any positive or negative number. Have another look at the function we called earlier :

_$ (setq sheet_type (vlax-make-safearray vlax-vbString '(0 . 2)))

This function created a single-dimension array consisting of three strings with a starting index of 0 (element 0, element 1 and element 2).
Consider this :

_$ (setq pt1 (vlax-make-safearray vlax-vbDouble '(1 . 3)))

The lower bound specified in this example is one and the upper bound specified is three, so the array will hold three doubles (element 1, element 2 and element 3).

The "vla-safearray-fill" function requires two arguments: the variable containing the array you are populating and a list of the values to be assigned to the array elements. You must specify as many values as there are elements in the array or vla-safearray-fill" results in an error.
The following code populates a single-dimension array of three doubles :

_$ (vlax-safearray-fill pt1 '(100 100 0))

To convert an array to an AutoLisp list, you can use the (vlax-safearray->list) function. Try it out :

_$ (vlax-safearray->list pt1)
(100.0 100.0 0.0)

Let's create a Array with two dimensions, each dimension with three elements :

_$ (setq two_dim (vlax-make-safearray vlax-vbString '(0 . 1) '(1 . 3)))
_$ (vlax-safearray-fill two_dim '(("Sheet1" "Sheet2" "Sheet3") ("a" "b" "c")))
_$ (vlax-safearray->list two_dim)
(("Sheet1" "Sheet2" "Sheet3") ("a" "b" "c"))

This is just a list of lists.
The first list, '(0 .1) is the number of dimensions.
The second list, '(1 . 3) is the number of elements

And now a three dimensional Array with two elements in each dimension :

_$ (setq three_dim (vlax-make-safearray vlax-vbString '(0 . 2) '(1 . 2)))
_$ (vlax-safearray-fill three_dim '(("Sheet1" "Sheet2") ("a" "b") ("d" "e")))
_$ (vlax-safearray->list three_dim)
(("Sheet1" "Sheet2") ("a" "b") ("d" "e"))

Here we have a list of three lists.
This time, the first list '(0 . 2) defines three dimensions and the second '(1 . 2) defines 2 elements in each dimension.

One place you will be using "vlax-safearray-fill" is when creating selection sets with filters. The syntax in ActiveX for "Selecting All with Filters" is as follows :

object.Select Mode[, Point1][, Point2][, Filter_Code][, Filter_Value]

Filter_Code must be an Integer array and Filter_Value a Variant array.
In Visual Lisp, the coding would be written like this :

;create a 2 element integer array for the DXF Codes.
(setq filter_code (vlax-make-safearray vlax-vbinteger '(0 . 1)))

;create a 2 element variant array for the values.
(setq filter_value (vlax-make-safearray vlax-vbvariant '(0 . 1)))

;DXF Codes for Objects and Layer : " 0" for Object," 8" for Layer.
(vlax-safearray-fill filter_code '(0 8))

;Name of Object and Layer.
(vlax-safearray-fill filter_value '("CIRCLE" "2"))

;select ALL Circles on Layer 2.
(vla-select newsset acSelectionSetAll nil nil filter_code filter_value)

For more information on Selections Sets, pop along to my "Selection Sets" tutorial page and get yourself even more confused.

The "vlax-safearray-put-element" function can be used to assign values to one or more elements of a safearray. The number of arguments required by this function depends on the number of dimensions in the array.

  • The first argument always names the safearray to which you are assigning a value.
  • The next set of arguments identifies index values pointing to the element to which you are assigning a value. For a single-dimension array, specify one index value: for a two-dimension array, specify two index values, and so on.
  • The final argument is always the value to be assigned to the safearray element.

Have a look at the following :

_$ (setq pt1 (vlax-make-safearray vlax-vbDouble '(1 . 3)))

_$ (vlax-safearray-put-element pt1 1 100)

_$ (vlax-safearray-put-element pt1 2 100)

_$ (vlax-safearray-put-element pt1 3 75)

_$ (vlax-safearray->list pt1)
(100.0 100.0 75.0)

_$ (vlax-safearray-put-element pt1 1 50)

_$ (vlax-safearray->list pt1)
(50.0 100.0 75.0)

Now let's populate a two-dimension array of strings :

_$ (setq two_dim (vlax-make-safearray vlax-vbString '(0 . 1) '(1 . 3)))

_$ (vlax-safearray-put-element two_dim 0 1 "a")

_$ (vlax-safearray->list two_dim)
(("a" "" "") ("" "" ""))

_$ (vlax-safearray-put-element two_dim 0 2 "b")

_$ (vlax-safearray-put-element two_dim 0 3 "c")

_$ (vlax-safearray-put-element two_dim 1 1 "d")

_$ (vlax-safearray-put-element two_dim 1 2 "e")

_$ (vlax-safearray-put-element two_dim 1 3 "f")

_$ (vlax-safearray->list two_dim)
(("a" "b" "c") ("d" "e" "f"))

You can use "vlax-safearray-get-element" to get the value of any element in any array. Here we'll use "vlax-safearray-get-element" to retrieve the second element in the first dimension of the array :

_$ (vlax-safearray-get-element matrix 1 2)

(vlax-safearray-get-l-bound) returns the lower boundary (starting index) of a dimension of an array :

Get the starting index value of the second dimension of the array :

_$ (vlax-safearray-get-l-bound two_dim  2) 

The second dimension starts with index 1.

Conversley, "vlax-safearray-get-u-bound" returns the upper boundary (end index) of a dimension of an array

Get the end index value of the second dimension of the array :

 _$ (vlax-safearray-get-u-bound two_dim  2) 

The second dimension ends with index 3.

You can use "vlax-safearray-get-dim" to get the number of dimensions in a safearray object :

Get the number of dimensions in "two_dim" :

_$ (vlax-safearray-get-dim two_dim) 

There are 2 dimensions in "two_dim".

Using Arrays

Let's have a look at putting some of this to good use :

c:Line_VL ( / acApp acDoc mspace p1 p2 sp ep lineObj)
   (setq acApp (vlax-get-acad-object))
   (setq acDoc (vla-get-activedocument acApp))
   (setq mspace (vla-get-modelspace acDoc))
   (setq p1 (getpoint "\nFirst Point : "))
   (setq p2 (getpoint p1 "\nSecond Point : "))
   (setq sp (vlax-make-safearray vlax-vbdouble '(0 . 2)))
   (setq ep (vlax-make-safearray vlax-vbdouble '(0 . 2)))
   (vlax-safearray-fill sp p1)
   (vlax-safearray-fill ep p2)
   (setq lineObj (vla-addline mspace sp ep))

There is an easier way of writing this routine :

(defun c:Line_VL ( / acApp acDoc mspace p1 p2 lineObj)
   (setq acApp (vlax-get-acad-object))
   (setq acDoc (vla-get-activedocument acApp))
   (setq mspace (vla-get-modelspace acDoc))
   (setq p1 (getpoint "\nFirst Point : "))
   (setq p2 (getpoint p1 "\nSecond Point : "))
   (setq lineObj (vla-addline mspace (vlax-3d-point p1) (vlax-3d-point p2)))

For methods that require you to pass a three-element array of doubles (typically to specify a point), you can use the "vlax-3d-point" function.

Well, that's it with Arrays. I haven't covered absolutely everything pertaining to Arrays, but you should now have enough to get you started. I hope that you understood everything I was warbling on about, and that I didn't confuse you too much!

Adios for now, amigo…