Fundamentals: Arrays
SmartCAM supports one-dimensional arrays of Integers, Decimals, and Strings. An array is a fixed-length list of same type data elements, where each list element can be accessed and modified discretely.
SmartCAM supports two types of arrays; a true one-dimensional array that works with all supported data types, and a Coordinate List which supports multi-dimensional arrays of decimal values but elements cannot be individually accessed or modified.
Arrays
An array variable is a useful way of storing multiple similar data types without having to declare a separate variable for each. For example, assume a macro creates a triangle. There are three corners to a triangle with two values for each; the X and Y location, three values if the macro supports the Z-axis as well.
Without array variables, a separate SmartCAM variable would need to be created for each number to be stored. Example:
// Variables to hold triangle coordinates
DECIMAL:#POINT1X = -2.0
DECIMAL:#POINT1Y = -2.0
DECIMAL:#POINT2X = 2.0
DECIMAL:#POINT2Y = -2.0
DECIMAL:#POINT3X = 0.0
DECIMAL:#POINT3Y = 2.0
// Draw the triangle
LINE[XS=#POINT1X, YS=#POINT1Y, ZS=0.0, XE=#POINT2X, YE=#POINT2Y, ZE=0.0]
LINE[XS=#POINT2X, YS=#POINT2Y, ZS=0.0, XE=#POINT3X, YE=#POINT3Y, ZE=0.0]
LINE[XS=#POINT3X, YS=#POINT3Y, ZS=0.0, XE=#POINT1X, YE=#POINT1Y, ZE=0.0]
Looking at the above example, it is easy to see the more complex the data types needed, the larger number of variables that would need to be created and tracked.
Arrays allow the programmer to create a list of like type data and reference the list elements individually. The following is the same triangle macro created using arrays.
// Arrays to hold triangle coordinates
DECIMAL:#XPOS[] = { -2.0, 2.0, 0.0 }
DECIMAL:#YPOS[] = { -2.0, -2.0, 2.0 }
// Draw the triangle
LINE[XS=#XPOS[0], YS=#YPOS[0], ZS=0.0, XE=#XPOS[1], YE=#YPOS[1], ZE=0.0]
LINE[XS=#XPOS[1], YS=#YPOS[1], ZS=0.0, XE=#XPOS[2], YE=#YPOS[2], ZE=0.0]
LINE[XS=#XPOS[2], YS=#YPOS[2], ZS=0.0, XE=#XPOS[0], YE=#YPOS[0], ZE=0.0]
Declaring Arrays
Like other variables, an array must have a specific type: decimal, integer, or string. Only data of the appropriate type can be stored in an array. One array cannot contain both integers and strings, for instance.
When declaring an array variable, the variable name is suffixed with square brackets
("[]
") to indicate that this variable is an array. The following example
shows several ways to declare array variables.
// Declare arrays without initial size or content
DECIMAL:#DECARRAY1[] // declares array of decimals of unknown size
INTEGER:#INTARRAY1[] // declares array of integers of unknown size
STRING:#STRARRAY1[] // declares array of strings of unknown size
// Declare array of known size and set initial element values
DECIMAL:#DECARRAY2[] = ALLOCATE(5, 1.0) // array size of 5, each element having value of 1.0
INTEGER:#INTARRAY2[] = ALLOCATE(5, 2) // array size of 5, each element having value of 2
STRING:#STRARRAY2[] = ALLOCATE(3, "Text") // array size of 3, each element containing the string "text"
// Declare array with different default values
DECIMAL:#DECARRAY3[] = { 1.0, 2.0, 3.0 } // array size of 3, with values: 1.0, 2.0, and 3.0
INTEGER:#INTARRAY3[] = { 2, 2, 5 } // array size of 3, with values: 2, 2, and 5
STRING:#STRARRAY3[] = { "Hello", "World" } // array size of 2, with values: Hello and World
Before an array can be accessed, the size or number of element items in the list, must
be defined. This can be handled with either the ALLOCATE()
command or by
assigning the list an initial set of data - which establishes the array size at the same
time.
INTEGER:#INTARRAY1[] // declares array of integers of unknown size
INTEGER:#INTARRAY2[] // another integer array declaration
// Defined arrays have no size. Allocate space for array
#INTARRAY1[] = ALLOCATE(10, 0) // allocates an array of size 10 with each having value of 0
#INTARRAY2[] = { 1, 2, 3, 4, 5 } // allocates an array of 5 with the values: 1, 2, 3, 4, 5
Accessing Array Elements
An array element is accessed by using a command with the following format:
#VARIABLE[INDEX]
. Thinking of an array as a list, the Index is
the position within the list that is needed. The index position into the list starts
as 0.
The following is the ubiquitous "Hello World" example again, this time using an array.
// Hello World with an array to hold the display strings
STRING:#SHW[] = { "Hello", "World" }
// display a message box with the formatted message
PAUSE[TX=#SHW[0] + " " + #SHW[1]]
When run, the message box will display the message: Hello World
.
The same syntax used to read an array element is also used to modify an array element.
// Hello World using an array with a modification
STRING:#SHW[] = { "Hello", "World" }
// Change World to Earth
#SHW[1] = "Earth"
// display a message box with the formatted message
PAUSE[TX=#SHW[0] + " " + #SHW[1]]
When run, this macro will display: Hello Earth
.
Resizing Arrays
When an array is declared, its size is allocated. For example:
INTEGER:#INTARRAY[] = ALLOCATE(10, 0)
In this example, the integer array INTARRAY
contains 10 elements.
This means that the programmer must know the maximum number of "slots" needed for any given array when the array is declared. This is not always possible. Therefore, SmartCAM allows an array to be resized or reallocated at any time. When reallocating the programmer can grow the size of the array - allow it to contain more elements - or reduce the size of an array.
The macro command for this is REALLOCATE(). REALLOCATE takes three parameters: the array to resize, the new number of elements, and the default value for any newly created array elements. It returns an integer that contains the new size of the array.
// Create an array and then reallocate it to add more elements
INTEGER:#NEWSIZE // new size of array
INTEGER:#INTARRAY[] = { 1, 2, 3 } // array has 3 elements, with the values: 1, 2, 3.
// Add two more elements with a default value of 0
#NEWSIZE = REALLOCATE(#INTARRAY[], 5, 0) // array has 5 elements, with the values: 1, 2, 3, 0, 0
In the above example, the array was originally declared with 3 elements, with the values of:
1, 2, and 3. Later, the size of the array is increased to 5 elements and the new elements have
a default value of 0. The #NEWSIZE
variable is set to "5" - because the array
now has five elements.
An array can be reduced in the same fashion.
// Create an array and then reallocate it to remove 2 items
INTEGER:#NEWSIZE // new size of array
INTEGER:#INTARRAY[] = { 1, 2, 3, 4, 5 } // array has 5 elements, with the values: 1, 2, 3, 4, 5.
// Reduce by two elements with a default value of 0
#NEWSIZE = REALLOCATE(#INTARRAY[], 3, 0) // array has 3 elements, with the values: 1, 2, 3
In this example, the size of the array was reduced by two elements.
Notice in both examples, the existing array values are not modified. When two additional array elements were added, the default value of 0 was only used for the newly added elements. When reducing the size of an array, the original array data was not modified - except for the elements that were eliminated when the array was resized.
SIZE()
The current size of an array can be found at anytime, using the SIZE() macro function. The SIZE() function takes a single parameter, the name of the array, and it returns an integer that gives the current size of the array.
// Get current size of array
INTEGER:#INTARRAY[] = { 1, 2, 3, 4, 5 }
INTEGER:#ISIZE
// Get size of INTARRAY
#ISIZE = SIZE(#INTARRAY[])
// Display size
PAUSE[TX="Size of array is " + FMT(#ISIZE, "T3.0")]
When run, the macro will display: Size of array is 5
.
Coordinate Lists
A Coordinate List is a special type of multi-dimensional array; once created the coordinate list can be passed into the polyline and spline macro commands, however the individual array elements cannot be accessed. Once created the coordinate list is fixed, it cannot be modified.
By default, the coordinate list contains 3-D data (X, Y, and Z components). The following example builds a 1x1 square using a coordinate list and the polyline macro command.
// Use a Coordinate List to create a 1x1 polyline square
#CLIST = { 0,0,0, 1,0,0, 1,1,0, 0,1,0 } // Create the four corners of the square
POLYLINE[CE=1, CL=#CLIST]
The CLIST
variable becomes a Coordinate List when it assigned the
3-D vector. A Vector is simply the coordinate data wrapped in the curly
brackets. Example: { 0,0,0,1,1,1 }
this is a vector.
For simple polylines and splines, the vector data can be supplied directly in the macro command, without using a coordinate list variable. The following is the same example macro, which creates a 1x1 box, without the coordinate list variable.
// Create a 1x1 polyline square using a vector
POLYLINE[CE=1, CL={ 0,0,0, 1,0,0, 1,1,0, 0,1,0 }]
The example vector contains four 3-D coordinates; the first is X=0, Y=0, Z=0, followed by X=1, Y=0, Z=0, and so forth. Since the dimensions of a vector are not defined, when using vector data directly in a spline or polyline, it must be 3-D coordinates.
If the coordinate list will contain other than the default 3-D coordinate data, it must be declared with the COORDLST[] macro command. The COORDLST[] command takes two parameters; the name of the variable, and the number of dimensions.
A coordinate list variable can store 2-D and 3-D data, as well as U,V data for 5-axis polylines.
The following example creates a 1x1 square using 2-D data and a 2-D coordinate list.
// Use a Coordinate List to create a 1x1 polyline square
COORDLST[VN="CLIST", DM=2] // DM=2 means 2-D data (XY coords without Z)
#CLIST = { 0,0, 1,0, 1,1, 0,1 } // Create the four corners of the square
POLYLINE[CE=1, CL=#CLIST]
Copying Coordinate Lists
The contents of one coordinate list can be copied to another coordinate list variable. The following example uses POLYLINE[] to create a 90 degree arc. The polyline data is created in one coordinate list and copied to a second for use in the POLYLINE[] command.
// Create 90 degree arc in a coordinate list
// Copy data to 2nd coordinate list before using
COORDLST[VN="ORIGCL", DM=3] // original coordinate list with 3-D data
COORDLST[VN="SECOND", DM=3] // second coordinate list, used in polyline
// Populate the original coordinate list
#ORIGCL = { COS(0.0), SIN(0.0), 0,
COS(30.0), SIN(30.0), 0,
COS(45.0), SIN(45.0), 0,
COS(60.0), SIN(60.0), 0,
COS(90.0), SIN(90.0), 0 }
#SECOND = { 0,0,0,
0,0,0,
0,0,0,
0,0,0,
0,0,0 }
// Copy contents of ORIGCL to SECOND
#SECOND = #ORIGCL
// Create the polyline
POLYLINE[CE=0, CL=#SECOND]
When run, a 90 degree arc is created. The polyline data for the arc is calculated
when the #ORIGCL
coordinate list is declared. It is then copied to the
other coordinate list (#SECOND
) and used in POLYLINE[]. This also points
out that in order to copy one coordinate list to another, the receiving coordinate
list must be declared and contain the same number of element slots as the originating
coordinate list.
COORDLST[VN="ORIGCL", DM=3] // original coordinate list with 3-D data
COORDLST[VN="SECOND", DM=3] // second coordinate list, used in polyline
// Populate the original coordinate list
#ORIGCL = { COS(0.0), SIN(0.0), 0,
COS(30.0), SIN(30.0), 0,
COS(45.0), SIN(45.0), 0,
COS(60.0), SIN(60.0), 0,
COS(90.0), SIN(90.0), 0 }
#SECOND = { 0,0,0,
0,0,0 }
// Copy contents of ORIGCL to SECOND - this will fail
#SECOND = #ORIGCL
This assignment will not work, since #SECOND
does not have enough array
elements defined to hold the contents of #ORIGCL
.
Calculating COORDLIST Data
In the previous examples, the coordinate list data is pre-calculated and assigned when the coordinate list is declared. Even in the 90 degree arc example, while the various SIN() and COS() functions are used, the results are calculated and number of coordinate list elements is fixed as declaration.
It is not possible to calculate the contents and number of elements for a coordinate list on the fly. For example, in a macro that generates a circle using polylines it is not possible to ask the user how many arc degrees per polyline segment to use.
However, it is possible to calculate this data in an array and copy the contents of an array into a coordinate list.
The following example prompts the user for the number of degrees per line segment and then creates the polyline circle.
// Create a circle using polyline
// Prompt user for number of degrees per polyline segment
DECIMAL:#DEGREES // holds user input
DECIMAL:#NUMSEGMENT // calculated number arc segments
DECIMAL:#PDATA[] // polyline data - calculated on the fly
DECIMAL:#COUNTER = 0.0
INTEGER:#ARRAYINDEX // keeps track of array index, number in []
// Ask user how many arc degrees per polyline segment
PROMPT[TX="How many degrees per segment?", VN="DEGREES"]
#NUMSEGMENT = 360.0 / #DEGREES
// Allocate data for array. Need 3 elements per arc point (XYZ) times # needed
#PDATA[] = ALLOCATE(INT(#NUMSEGMENT * 3.0), 0.0)
// Loop and create circle data
WHILE(#COUNTER < #NUMSEGMENT)
#ARRAYINDEX = #COUNTER * 3 // treating 1-D array like a 3-D array
#PDATA[#ARRAYINDEX] = COS(#COUNTER * #DEGREES)
#PDATA[#ARRAYINDEX+1] = SIN(#COUNTER * #DEGREES)
#PDATA[#ARRAYINDEX+2] = 0.0
#COUNTER = #COUNTER + 1
ENDW
// Create Coordinate List and copy array contents
COORDLST[VN="ARCCL", DM=3]
#ARCCL = #PDATA
// Create the circle
POLYLINE[CE=1, CL=#ARCCL]
// Display extents
FULL[]
When run, user is prompted for number of degrees per segment, use 36. This will create a circle with 10 segments (36 degrees each).
At the moment, Coordinate lists and Arrays are not interchangeable. When a macro command expects a coordinate list, it will not accept an array. Therefore, the array data must be assigned to a coordinate list and the coordinate list used as a parameter.
The coordinate list assignment is not equivalent; it is possible to assign an array to coordinate list, however, it is not possible to assign a coordinate list to an array.
Finding All Layers, Steps, and Tools
Array-based macro functions exist that will return a list of Layers, Steps, and Tools. These functions are useful when searching for a specific Step, Tool, or Layer. The Steps, Tools, Layers do not need to be used - meaning, they do not need to be assigned to geometry - but they do need to exist. In the case of Layers, the layer needs to either be assigned to geometry or have a description to be found.
These functions are unique, in that they return their results in an array. When used, they will deallocate the destination array (if it had previously had memory allocated for it) and create the array with just enough member elements to hold the requested list of data.
The following macro will find and display a list of all the defined Steps in the current process model. Use this as an example of how to work with these macro functions.
// Display all Steps in current process model
INTEGER:#STEPARRAY[] // array to hold results
INTEGER:#STP_CNTR=0 // counter for loop to aggregate the list for display
INTEGER:#ARR_SIZE=0 // size of Step Array
STRING:#Out_Buffer // display string
// Get list of defined Steps
#STEPARRAY[] = ALL_STEPS()
// Get size of array
#ARR_SIZE = SIZE(#STEPARRAY[])
// Add header text to output string
#Out_Buffer = "List of Steps: "
// Loop thru array and get Step numbers
WHILE(#STP_CNTR < #ARR_SIZE)
#Out_Buffer = #Out_Buffer + "[" + ITOA(#STEPARRAY[#STP_CNTR]) + "] "
#STP_CNTR = #STP_CNTR + 1
ENDW
// Display results
PAUSE[TX=#Out_Buffer]
If the current process model contained Steps 10, 20, and 40; the output from this macro would be:
List of Steps: [10] [20] [40]
The ITOA() function is used to convert the Integer array element into a ASCII string for addition to the output string and later display.
Related Topics