# A Replacement for VB Evaluate(string)

To graph a function we need to evaluate a string into its math equivalent. Excel VBA has the Application.Evaluate method which takes a variant for an argument and will evaluate a wide range of math symbols, with or without quotations – whatever is between the parentheses. In Visual Studio that doesn’t exist. We want to type a function (the right side of Y = some expression of X) into a textbox on a form, save it to a string variable, then calculate the value to a double for a range of X values. For instance,

strfunc = “X^3 – 3*X^2 + 2*x + 2”

A loop calculates X and Y values starting at X minimum, incrementing by a small X increment, ending at X maximum, and saving all the values to an array to be an argument for AddLightWeightPolyline.

the number of points to be stitched into a graph is calculated –
numpts = CInt((Xmax – Xmin) / Xinc) ‘number of line segments

there is always one more point than line segments
numpts = numpts + 1

the array of doubles is sized –
ReDim Pt(0 To numpts * 2 – 1) ‘store x and y for one pt

after the number of points has been calculated, and the array is ready, the loop is

For i = 1 To numpts
x = Xmin + ((i - 1) * Xinc)
y = eval_func(strfunc, x)
pt(i * 2 - 2) = x
pt(i * 2 - 1) = y
Next i


X is recalulated each time through the loop. Rather than adding the increment to a running sum, its more accurate to use the loop counter to multiply the increment, because of the imprecision of doubles. With odd numbers you might eventually run one cycle short or long. The loop counter should always be an integer. Calculation of Y is handed off to a function with the function string and the current value of X. The function substitutes the numerical value of X whereever X the symbol appears.

Debug.Print strfunc
strfunc = Replace(strfunc, “x”, x, , , vbTextCompare)
Debug.Print strfunc

the first time thru where X is at the starting value of, say, -6, the string and the modified string look like –

X^3 – 3*X^2 + 2*x + 2
-6^3 – 3*-6^2 + 2*-6 + 2

then the final scalar value is calculated and the value returned as Y.

dbl_result = Evaluate(strfunc)

This code was being run from Excel. Evaluate was a method of the top level Application.

For Visual Studio VB.Net we need a replacement for Evaluate.

A lot of people have asked this question. There are quite a few homebuilt parser class solutions. Another method is to link to another language and use its Eval method. A quick easy solution was found, credit to user kleinma

As kleinma says, “Set a reference in your app to “Microsoft Script Control 1.0″ It is under the COM tab in references, not in the .NET tab”

A lot of testing will show if its a decent fix or just the first try.

    Public Xmin As Double
Public Xmax As Double
Public Xinc As Double
Public Ylim As Double
Public g_stop As Boolean
Public g_err_counter As Integer

Function calc_xy(strfunc As String) As Double()
'returns an array of x and y doubles
'Xmax, Xmin, Xinc, Ylim are all assumed to have values
Dim x, y As Double
Dim i, numpts As Integer
Dim Pt() As Double

g_err_counter = 0
g_stop = False

numpts = CInt((Xmax - Xmin) / Xinc) 'number of line segments
numpts = numpts + 1   'one more pt than line segment
ReDim Pt(0 To numpts * 2 - 1) 'store x and y for one pt

For i = 1 To numpts
x = Xmin + ((i - 1) * Xinc)
y = eval_func(strfunc, x)

If g_stop Then  'if eval_func set g_stop then
MsgBox("errorcounter at 3 - exiting")
Exit For
End If
Pt(i * 2 - 2) = x
Pt(i * 2 - 1) = y
Next i

Return Pt
End Function

Function eval_func(ByVal strfunc As String, x As Double) As Double
'returns a double
'on error returns Ylim but sets a flag to stop after 3 errors
'which would indicate a bad formula
'one or two error is ok because havent solved the mid-graph asymptote yet
Dim result As Double
strfunc = Replace(strfunc, "x", x.ToString, , , vbTextCompare)

Dim SC As New MSScriptControl.ScriptControl
SC.Language = "VBSCRIPT"
Try
result = Convert.ToDouble(SC.Eval(strfunc))
Catch ex As Exception
MessageBox.Show(ex.GetType.ToString & " " & ex.Message)
g_err_counter = g_err_counter + 1
If g_err_counter > 2 Then
g_stop = True
End If
result = Ylim
End Try

Return result
End Function


# An Issue with Regions

A problem has found me with creating Regions, probably the first one that has come up with accessing Autocad COM objects from Visual Studio VB.Net. There is probably a fix, but for the time the workaround lets me go on. It involves arrays of autocad objects. It is not a general problem that involves all arrays of autocad object types, just the problem visual studio is having reading the output of Addregion. I will document it here including the similar but fixable problem of output of arrays of visual studio data types.

The fixable problem is like this –

sample VBA code from autodesk to create polylines (with intermediate steps removed)

Dim get2Dpts As Variant
get2Dpts = pline2DObj.Coordinates

on paste, visual studio informs
‘Variant is no longer a supported type; use the ‘Object’ type instead.

changing get2Dpts to Object does return to the variable without error. At edit time, Visual Studio is recognizing a generic object not an array coming out of the Coordinate property.

Dim get2Dpts As Object

but generates the error if an attempt is made to read the variable
get2Dpts(0)
Option Strict On disallows late binding.

but at runtime, in the immediate window,
? typename(get2dpts)
“Double()”
? typename(get2dpts(0))
“Double”

so visual studio recognizes the returned object as an array of doubles during execution but not before. Turning Option Strict Off (My Project, Compile tab) solves the problem but i have learned a lot about how variables work with it on and dont accept that as a real solution.

more to the point change the variable declaration to an array of doubles.

Dim get2Dpts() As Double

now we get the error at

get2Dpts = pline2DObj.Coordinates
Option Strict On disallows implicit conversions from ‘Object’ to ‘Double()’.

we can fix that and we are done (and the immediate window returns the same object types as before).

get2Dpts = CType(pline2DObj.Coordinates, Double())

now for the currently unfixable problem involving an array of autocad objects instead of an array of doubles –
lets navigate to the autodesk ActiveX Reference Guide and paste in the Sub Example_AddRegion, make the usual fixes and hook up autocad,

    Sub Example_AddRegion()
' creates a region from an arc and a line.

Dim centerPoint(2) As Double
Dim radius, startAngle, endAngle As Double

centerPoint(0) = 5.0# : centerPoint(1) = 3.0# : centerPoint(2) = 0#
startAngle = 0
endAngle = 3.141592

' Define the line

' Create the region
Dim regionObj As Variant
End Sub


Besides the expected Variant problem, i get an error on AddArc return but not AddLine.

fix with

and now the error goes down to addline, fix the same way, except it still does not like the array indexed inside the Addline parameter list.

i could probably nest two more CTYPE inside, but lets back up and simplify.

       Dim arcobj As AcadArc



now the errors are reduced to the variant where AddRegion returns an array of region objects. I cannot solve this at present.

first move

this gives the expected error –
Option Strict On disallows implicit convsersions from ‘Object’ to ‘AcadRegion()’.

based on the experience with the Coordinate property of Polyline returning an array of doubles, this should work,

but it errors out at Ctype AFTER it creates the Region, it cannot pass the array of Regions to the object. the error is –

Lets change the Dim statement to Object. We get no errors and a successful run,
Dim regionObj As Object

but we are not accessing the returned variable. any attempt to use properties of a region fail at the intellisense level, there are no properties other than a few generic object properties.

does not happen – regionobj.EntityTransparency = “50”

in runtime at the immediate window –

? typename(regionobj)
“Object()”
?typename(regionobj(0))

compare to

? typename(get2dpts)
“Double()”
? typename(get2dpts(0))
“Double”

this doesnt help, i still get ‘unable to cast’ exception

visual studio only recognizes an array of primitive objects coming out of addregion. I have tried various ways to read the array into an autocad variable and failed.
the workaround is to create the region but not capture it to a variable. use a subroutine to create a new selection set, get the last item in the drawing, read that into a proper variable.

        Dim regionobj As AcadRegion

ss.Select(AcSelect.acSelectionSetLast)

regionobj.EntityTransparency = "50"


at edit, visual studio recognizes an object coming out of Coordinates property, but at runtime it recognizes the object is actually an array of doubles, and its able to cast the object to an array of doubles at edit.

at edit, visual studio recognizes an object coming out of AddRegion, but at runtime it recognizes the object as an array of objects, and its not able to cast the object to an array of AcadRegions at edit.

thats it. i can not say now there are no problems or how wide it is. its a possibility i did not hit the right combination of keys, or the solution may be some kind of library link to give visual studio enough information to handle the output of addregion.

    Sub Example_AddRegion()
' creates a region from an arc and a line.

Dim centerPoint(2) As Double
Dim radius, startAngle, endAngle As Double

centerPoint(0) = 5.0# : centerPoint(1) = 3.0# : centerPoint(2) = 0#
startAngle = 0
endAngle = 3.141592

ss.Select(AcSelect.acSelectionSetLast)

regionobj.EntityTransparency = "50"

End Sub


# Autocad Application Object in Visual Studio

The root or the trunk of the autocad COM object model is the AcadApplication object. It has to be obtained first to get anything else. If Autocad is running and you know it is running it can be obtained with GetObject.

        Dim app As AcadApplication


Autocad.Application.22 is a version specific program ID. you can also use a version independent string, Autocad.Application, but if you have more than one autocad version loaded, it will load the last one used and saved, probably.

here is the Autocad Application object Help page.

the clickable object model is easier to use as a reference base.

I cannot tell that any updates have been made for a long time so the exact year does not matter.

the easiest reference of all and its exactly the same material are the ActiveX API documentation found locally in these folders:

In Visual Studio with Strict Typing On I added the Ctype function to get rid of the error.

‘Option Strict On disallows implicit conversions from ‘Object’ to ‘AcadApplication’

In autocad VBA help, the root object is “ThisDrawing.Application”. This is because ThisDrawing is provided by the implementation as always available. ThisDrawing is AcadApplication.ActiveDocument. You can run all the sample code without changing that part of it by declaring an AcadDocument variable called ThisDrawing.

        Dim ThisDrawing As AcadDocument
ThisDrawing = app.ActiveDocument


The only subtle difference, is that your ThisDrawing will not automatically follow a shifting ActiveDocument. Its only current if the ActiveDocument is not changed.

In the clickable object model, objects are noted with an oval, collections are noted with a rectangle. To get to the drawing objects, the path is Application – Documents – Document. ActiveDocument is a property of the Application, a shortcut to the main drawing entities.

here is an Application test file where i demo’ed many of the application methods and properties. most of the actual code is pasted from the help, going thru the application page and clicking on the method or property. i commented out much of it after i ran it once.

 Sub App_Test1()

ThisDrawing = app.ActiveDocument

'GetInterfaceObject
'Option Strict On disallows implicit conversions from 'Object' to 'AcadAcCmColor'
Debug.Print("GetInterfaceObject ColorMethod: " & col.ColorMethod.ToString())

'Quit
'Closes the drawing file And exits the AutoCAD application.
'app.Quit()

'ZoomWindow
'Dim pt1() As Double
'Dim pt2() As Double
'pt1 = Pt(1.3, 7.8, 0)
'pt2 = Pt(13.7, -2.6, 0)
'app.ZoomWindow(pt1, pt2)

'ZoomScaled
'Dim scalefactor As Double
''help exampe - Dim scaletype As Integer
'Dim scaletype As AcZoomScaleType
'scalefactor = 2
'scaletype = AcZoomScaleType.acZoomScaledRelative
'app.ZoomScaled(scalefactor, AcZoomScaleType.acZoomScaledRelative)
'app.ZoomScaled(scalefactor, scaletype)

'ZoomAll
'ZoomExtents
'ZoomPickWindow
'ZoomPrevious
app.ZoomExtents()
app.ZoomPickWindow()
app.ZoomAll()
app.ZoomPrevious()

'ZoomCenter
'Dim zcenter() As Double
'Dim magnification As Double
'zcenter = Pt(3, 3, 0)
'magnification = 10
'app.ZoomCenter(zcenter, magnification)

'Update
app.Update()

'' It then creates a new menu item and inserts it at the bottom of the File menu.
'' The menu item will be automatically removed when AutoCAD is restarted
'Dim openMacro As String
'' assign an Open macro (VBA equivalent of: "ESC ESC _open ")
'openMacro = Chr(3) & Chr(3) & Chr(95) & "open" & Chr(32)

' into it. The menu is then displayed on the menu bar.
' The menu item will be automatically removed when AutoCAD is restarted
'' Create the new menu in this group
'' assign an Open macro (VBA equivalent of: "ESC ESC _open ")
'openMacro = Chr(3) & Chr(3) & Chr(95) & "open" & Chr(32)

'Preferences
' This example returns the current setting of
' LogFilePath from the preferences object.
preferences = ThisDrawing.Application.Preferences
' Retrieve the current LogFilePath value
MsgBox("The current value for LogFilePath is " & preferences.Files.LogFilePath, , "Preferences Example")

'Documents
' This example obtains a reference to the Documents collection
Dim msg As String
msg = vbCrLf

'' Cycle through the Documents collection and retrieve the names
For Each Document In app.Documents
msg = msg & Document.Name & vbCrLf
Next

If app.Documents.Count > 0 Then
MsgBox("The loaded documents are: " & msg)
Else
End If

' This example reads and displays the current window state of the AutoCAD application.
Dim CurrentState As String = ""
' Use the "WindowState" variable to determine the window state of AutoCAD
Select Case app.WindowState
Case AcWindowState.acMin : CurrentState = "Minimized"
Case AcWindowState.acMax : CurrentState = "Maximized"
Case AcWindowState.acNorm : CurrentState = "Normal Size"
End Select

' Display window state
MsgBox("AutoCAD is now: " & CurrentState)

End Sub


# Point Data

Point Data

Here is the cumbersome addline method (ActiveX aka VBA) autocad knowledge network. I have simplified the point names.

Dim pt1(0 To 2) As Double
Dim pt2(0 To 2) As Double
pt1(0) = 1#: pt1(1) = 1#: pt1(2) = 0#
pt2(0) = 5#: pt2(1) = 5#: pt2(2) = 0#

When I first started autocad VBA I emailed an autodesk blog writer and suggested to him this wordiness practically eliminated any possibility of useful or popular parametrics and why not develop some simplified techniques in a column. He replied, first of all I have not used VBA in years, and secondly, I don’t have time to give you support. Alright fine. Stay away from my daughter. They seem to have solved the problem in dot net, but added another realm of issues. Here is the relevant line to create a Line object in (.NET), only one line of many.

Using acLine As Line = New Line(New Point3d(5, 5, 0), New Point3d(12, 3, 0))

You can do that in VBA. The std VBA help above is rewritten as –

Dim pt1() As Double, pt2() As Double
pt1 = pt(1, 1, 0)
pt2 = pt(5, 5, 0)

To do the shorthand method as in the (.NET) example with the inline assignment –

The (.NET) example also has 12 other lines to connect to the autocad unit. My program would also have about that number of lines, except I sub out once per session using a global object variable so the object keyword ThisDrawing becomes AcadDoc, and its invoked as

Whats happening here is that the old method of declaring points is using a static array, and the new method is using a dynamic array. A static array cannot be assigned to a variable.

pt1 = pt2

Will give an error if pt1 was declared a static array, and it will be fine if it was a dynamic array. Pt2 can be either in this example.

The function to set point data then is always used, just as the (.NET) coders did with Point3D. There is no downside. The variable is always declared as a dynamic array.

Dim pt1() as double
pt1 = pt(1,2,3)

The function takes the xyz values and returns a dynamic point array used everywhere a static point would be used. In fact it declares a static array, but then passes that to a dynamic array. This little function opens up a world of productivity.

Function pt(x As Double, y As Double, z As Double) As Double()
Dim pnt(0 To 2) As Double
pnt(0) = x: pnt(1) = y: pnt(2) = z
pt = pnt
End Function


Wrapper functions to draw lines, circles, text, dimensions, etc can always pass point arrays rather than xyz values for simplicity. Its easier to develop the points in the calling function even if they need to be deconstructed in the called function.

A simple orthogonal 2D box, a very common item, is defined by two points on opposite corners. It requires two x values and two y values. The function is designed with an illustration that shows the box being drawn a particular direction, but in practice any two random points define one and only one orthogonal rectangle, providing they have neither x or y in common. The lightweight polyline is used in this case. Points are passed but then the xy values are extracted and formatted for the polyline array.


Sub mbox(p1() As Double, p2() As Double)
Dim pts(0 To 7) As Double

Dim x0 As Double, x1 As Double
Dim y0 As Double, y1 As Double

x0 = p1(0)
x1 = p2(0)
y0 = p1(1)
y1 = p2(1)

pts(0) = x0: pts(1) = y0
pts(2) = x1: pts(3) = y0
pts(4) = x1: pts(5) = y1
pts(6) = x0: pts(7) = y1
objent.Closed = True
objent.Layer = "0"
End Sub



This illustrates how the 2D polyline works with an array of alternating x and y values. It doesnt use the point function, but the calling sub does with little problem.

Dim pt1() as double, pt2() as double
pt1 = pt(6, 1, 0)
pt2 = pt(2, 5, 0)
mbox pt1, pt2

The same thing could be drawn with lines with pts passed and used directly. I like objects that stay connected. The closed polyline takes an array twice the size of the number of vertexes consisting only of x and y values. The x and y values are the data used to draw. The parameters are the concept variables that push the x and y values around. For instance a sheet metal channel, would have 3 variables – the width, the leg, and the thickness of the material. Lets draw one where each leg can vary – 4 variables.

First step is to draw the object in the same rotation the program will use and label all x and y values. The easiest place to draw the object is with x0 and y0 at 0,0, so I assume that but you could draw it in place with a little more rigor. These type of sketches are not complete until dimensions, text, screws, notes, holes, other block details are added, and the whole assembly moved into a print border. I draw those using the xy data, and move the entire assembly into 8×10 print format all at once. If I have a list, I move that bunch over (in program) and start a new piece.

Looking at this sketch, we know we need a J_CHAN sub passing A, B, C and W for parameters.

The first thing the sub does is declare the x and y variables needed, and a dynamic array to put them into. x values then y values are specified in terms of A, B, C and W.

Sub J_CHAN2(A As Double, B As Double, C As Double, W As Double)
‘A is WIDTH, B is FRONT VERT, C is BACK VERT

Dim x0 As Double, y0 As Double
Dim x1 As Double, x2 As Double, x3 As Double
Dim y1 As Double, y2 As Double, y3 As Double
Dim pts As Variant

x0 = 0
y0 = 0

x1 = W
x2 = W + A
x3 = 2 * W + A

y1 = W
y2 = W + B
y3 = W + C

Now we have all the data to draw the object. The problem now is getting it to the 2D Polyline.
Addlightweightpolyline method takes an array of doubles. We used it with the Box sub. It has the same syntax as the old point method. Its a static array. It has the same cumbersome requirements. Every shape that has a different number of vertices requires the proper size array.

Here is the autocad help example. I added connect_acad and changed ThisDrawing to acadDoc to get it to run (from excel). This draws a 4-line squiggle, using 5 points (for some reason 2 segments are in line). So you need 10 places in the array, 0 to 9.


' This example creates a lightweight polyline in model space.

Dim points(0 To 9) As Double

' Define the 2D polyline points
points(0) = 1: points(1) = 1
points(2) = 1: points(3) = 2
points(4) = 2: points(5) = 2
points(6) = 3: points(7) = 2
points(8) = 4: points(9) = 4

' Create a lightweight Polyline object in model space
ZoomAll

End Sub


The easiest way to make an array in VBA is with the Array function. The array function returns a variant containing an array. The syntax is very simple. You do not have to index the locations.

Dim Ar as variant
Ar = Array(1,1,1,2,2,2,3,2,4,4)

The data is the same as above but the array is not the same. Polyline won’t accept it. “Invalid argument.” It’s a variant that contains an array. VBA won’t assign the variant to a dynamic array either. We have to step through it to convert it to an array of doubles that polyline will use. But that gives us a chance to write a sub that will accept an array of any length. So we can make our point list with the simple array function, then hand it off to a sub to draw.

Sub test_array()
Dim Ar As Variant

Ar = Array(1, 1, 1, 2, 2, 2, 3, 2, 4, 4)

draw_ar Ar
End Sub

Sub draw_ar(pt As Variant)
Dim pt2() As Double
Dim i As Integer
Dim lower As Integer, upper As Integer
lower = LBound(pt)
upper = UBound(pt)

ReDim pt2(lower To upper)
For i = lower To upper
pt2(i) = pt(i)
Next i

' objent.Closed = True
objent.Update
End Sub



I have the polyline not closed to duplicate the autocad help example. Now my draw sub will draw any list given to it of any length and I can randomly add pairs of numbers to the list to test it.

Lets go back to our J-Channel and make the array of x and y values with the Array function then pass it off to be drawn. Final working version here.

Sub test_chan()
Call J_CHAN2(4, 2, 3, 0.0625)
End Sub

Sub J_CHAN2(A As Double, B As Double, C As Double, W As Double)
'A is WIDTH, B is FRONT VERT, C is BACK VERT

Dim x0 As Double, y0 As Double
Dim x1 As Double, x2 As Double, x3 As Double
Dim y1 As Double, y2 As Double, y3 As Double

Dim pts As Variant

x0 = 0
y0 = 0

x1 = W
x2 = W + A
x3 = 2 * W + A

y1 = W
y2 = W + B
y3 = W + C

pts = Array(x0, y0, x3, y0, x3, y3, x2, y3, x2, y1, x1, y1, x1, y2, x0, y2, x0, y0)
Call draw_array(pts)
global_pline.Layer = "0"

End Sub

Sub draw_array(pt As Variant)
Dim pt2() As Double
Dim i As Integer
Dim lower As Integer, upper As Integer
lower = LBound(pt)
upper = UBound(pt)

ReDim pt2(lower To upper)
For i = lower To upper
pt2(i) = pt(i)
Next i

objent.Closed = True
objent.Update

Set global_pline = objent
End Sub


Point data starts with x and y values. X and Y and Z values are loaded into point arrays.

# Polygon Circle

Regular polygons are called convex and star polygons that cross themselves are non-convex. For both types the vertexes (vertices) are on a circle that has a center and radius.

The turning angle the turtle uses from line to line is more generally called the exterior angle of a polygon. Its the angle from the line extended to the next line.

The sum of the exterior angles of a regular polygon = 360.

$nA=360$

To find the radius of a regular polygon, divide it into triangles. each wedge has a central angle of 360/n.

Bi-Secting one of these triangles creates a right triangle with a known angle and a known side length.

This formula works for regular polygons and for star polygons. If a line is drawn from the center to each vertex, and a line drawn perpendicular to the edge, right triangles with known sides and angles are created.

now that we have the radius, the center point can be found. Think of the turtle on a vertex, heading turned to the next line. It is on a triangle we already solved to find the radius. we use the same angles. The angle that the turtle needs to turn to look at the center is 90-A/2. I use acad utility polarpoint, not the turtle, to draw the circle. I wrote a new turtle class function to return the current turtle position as a point using a dynamic array and used it directly in PolarPoint.

Sub poly_1(angle As Double, n As Integer, len_side As Double)
Dim inc As Integer
Dim A As Double
Dim ctr() As Double
Dim ang2ctr As Double

rad = len_side / (2 * (Sin(A / 2)))

For inc = 1 To n
turtle1.fd len_side
turtle1.left angle
Next inc

txt_h "A = " & angle, turtle1.x1, turtle1.y1, 0.125
txt_h "n = " & n, turtle1.x1, turtle1.y1 - 0.25, 0.125
txt_h "R = " & (angle * n / 360), turtle1.x1, turtle1.y1 - 0.5, 0.125
txt_h "Radius = " & Round(rad, 4), turtle1.x1, turtle1.y1 - 0.75, 0.125

End Sub

'this is part of the turtle class module to return current position as a point array
Public Function pt1() As Double()
Dim pnt(0 To 2) As Double
pnt(0) = Me.x1: pnt(1) = Me.y1: pnt(2) = 0
pt1 = pnt
End Function



I am not going to try to draw these triangles, but all the graphics check out, it seems like my formulas derived from simpler polygons gives me the correct center and radius.


Sub turtle_demo_18()
init_turtle
Dim n As Integer, R As Integer
Dim A As Long

A = 164
n = LCM(A, 360) / A
R = LCM(A, 360) / 360

Call poly_1(CDbl(A), n, 1)
End Sub



# polygons

The inputs for this example problem are the angle 174 and 360. When a multiple of 174’s add up to a multiple of 360, the program stops. In advance we do not know how many 174s will add up to how many 360s, but we want the first one. This is the Least Common Multiple of Angle A (174) and 360.
LCM (A, 360)

174 has prime factors of 2 * 3 * 29
360 has prime factors of 2 * 2 * 2 * 3 * 3 * 5
the least common multiple is 360 * 29 = 10440.

we can substitute that into either side of the equation
$nA=360R$
$n*174 = 10440$
$10440 = 360*R$
to find that n = 60 and R = 29

The LCM can be figured different ways. There is a famous method of Euclid, and Euler, (no Eugene). There is an excel function to do it. Sample VBA code can be downloaded. The simplest method conceptually is the same way the turtle does it, by adding angles one by one and testing the result.

Here is brute force way one with no advance knowledge of when the program will stop. The program stops when the turtle heading returns to its original position. I also have a second emergency stop at 361 which never comes into play usually. (*footnote – the examples in the group photo that look like folded ribbon were polygons that did not close until 360 lines were drawn but were forced into an early exit with ” If inc > 130 Then Exit Do”) (**footnote – if the turn angle A is an integer, the maximum number of lines to bring the heading back to start is 360.)

Sub poly_360(len_side As Double, angle As Double)

Dim inc As Integer

Do
turtle1.fd len_side
turtle1.left angle

inc = inc + 1
If inc > 361 Then Exit Do

End Sub


Here is a primitive LCM function based on the same method, not intended to be the final version. The angles are added one by one and the result divided by 360 looking for a remainder, breaking out when the remainder is zero. The VBA mod operator works accurately only with integers. I had some overflows on the multiplication. The function seems to work better when all is type Long.

Function LCM(A As Long, B As Long)
Dim n As Long
Dim result As Long
Dim remainder As Long
n = 0

Do
n = n + 1
result = n * A
remainder = result Mod B
Loop While remainder <> 0

LCM = result
End Function


Now the sub to draw the polygon can be taken back to its roots. The loop calculations can be removed, because we will know in advance how many lines will be drawn.

Text information labels are added after the drawing is complete.

Sub poly_1(angle As Double, n As Integer, len_side As Double)
Dim inc As Integer

For inc = 1 To n
turtle1.fd len_side
turtle1.left angle
Next inc

txt_h "A = " & angle, turtle1.x1, turtle1.y1, 0.125
txt_h "n = " & n, turtle1.x1, turtle1.y1 - 0.25, 0.125
txt_h "R = " & (angle * n / 360), turtle1.x1, turtle1.y1 - 0.5, 0.125
End Sub


The sub to call the poly can be fancy or plain. It can draw families of polygons. It can loop and draw a range of turning angles.
This particular one will draw all the polygons with total turns (R) = 29 of angles between 1 and 180.

Sub turtle_demo_16()
init_turtle

Dim inc As Integer
Dim n As Integer, R As Integer
Dim A As Long, B As Long

B = 360

For inc = 1 To 180

A = inc
n = LCM(A, B) / A
R = LCM(A, 360) / 360

If R = 29 Then

Debug.Print "LCM of " & A; " and " & B; " = " & LCM(A, B)
Debug.Print "A = " & A
Debug.Print "n = " & n
Debug.Print "R = " & R
Debug.Print " "

Call poly_1(CDbl(A), n, 1)

turtle1.x1 = turtle1.x1 + 5

End If
Next inc

End Sub


Sub turtle_demo_16()
init_turtle

Dim inc As Integer
Dim n As Integer, R As Integer
Dim A As Long, B As Long
B = 360

For inc = 160 To 181
A = inc
n = LCM(A, B) / A
R = LCM(A, 360) / 360
Call poly_1(CDbl(A), n, 1)
turtle1.x1 = turtle1.x1 + 1.1
Next inc

End Sub


$R<20$

# test 2-7-2019

just testing and experimenting with latex math typesetting, don’t read.

https://en.wikibooks.org/wiki/LaTeX/Mathematics

the basic syntax –
$\alpha A \beta B \gamma \Gamma \pi \Pi \phi \varphi \mu \Phi$

$\alpha A \beta B \gamma \Gamma \pi \Pi \phi \varphi \mu \Phi$

**************************

size parameter 1 –
$\alpha A \beta B \gamma \Gamma \pi \Pi \phi \varphi \mu \Phi$

size parameter 2 –
$\alpha A \beta B \gamma \Gamma \pi \Pi \phi \varphi \mu \Phi$

size parameter 3 –
$\alpha A \beta B \gamma \Gamma \pi \Pi \phi \varphi \mu \Phi$

size parameter 4 –
$\alpha A \beta B \gamma \Gamma \pi \Pi \phi \varphi \mu \Phi$

size parameter 5 does not display.

***************************************

$\leq \subset < > \geq = \approx \neq \parallel \in \perp \sphericalangle \notin \measuredangle$

$\pm \times \ast \div$

***************************************************

+ – = ! / ( ) [ ] | ‘ : *

$+ - = ! / ( ) [ ] | ' : *$

*************************************************************

$\sin \cos \tan \cot \sec \csc$

$\sin \cos \tan \cot \sec \csc$

$\cos (2\theta) = \cos^2 \theta - \sin^2 \theta$

***************************************************************************

$\lim\limits_{x \to \infty} \exp(-x) = 0$

****************************************************************************

$3\times{}^1/_2=1{}^1/_2$

$\tan \theta + 2= \frac {\sin \theta} {\cos \theta} + 2$

***************************************************************************

$\sqrt{\frac{a}{b}}$

$\sqrt[n]{1+x+x^2+x^3+\dots+x^n}$

********************************

https://en.wikibooks.org/wiki/LaTeX/Mathematics

********************************

# Specific method for parametric drawing programs

The hard part of coding parametric drawing program whether in lisp or VBA is managing the large number of points. The program turns into many lines of hard to read data apparently randomly named. A sketch has to be made with points labeled and equations or formulas entered. It all might make sense during the coding, but probably won’t a few weeks later when a change has to be made even if the sketch(s) is found. It won’t be obvious how the points are calculated or why lines are drawn from pt7 to pt21 to pt3. There is no one right way but I have recently worked on both lisp and vba programs and have some specific but not comprehensive suggestions. This is a special theory for creating the xy data but not a general theory for the entire program.

There are two basic ways to manage your drawing subroutine. It can accept points or xy data. Try both ways. Both methods need xy data.

In Lisp I use Visual Lisp objects rather than the “command” method. The object method can draw directly in to a block definition, and it can directly change the layer property. It requires a point object, but that can be created and passed as a parameter or the xy data can be passed and the point created in the subroutine.

For lisp I made a point creation routine and passed points to the subroutine which runs the Addline method.

(defun pt ( x y ) (vlax-3d-point x y 0))

In a very simple box example this gets called as

(setq pt1 (pt 0 0) pt2 (pt L 0) pt3 (pt L W) pt4 (pt 0 W))

Then the line routine would be

(defun linep (pt1 pt2 obj lyr / lineobj) (setq lineobj (vla-AddLine obj pt1 pt2)) (vla-put-layer lineobj lyr) )

And be called as

(linep pt1 pt2 ms "hidden")

Or you could pass xy data

(defun line (x1 y1 x2 y2 obj lyr / pt1 pt2 lineobj) (setq pt1 (vlax-3d-point x1 y1 0) pt2 (vlax-3d-point x2 y2 0)) (setq lineobj (vla-AddLine obj pt1 pt2)) (vla-put-layer lineobj lyr) )

In VBA every variable has to be declared previous to use, so you might lean towards passing xy data. Assume you want to draw a notched rectangle and make it a polyline. You make a sub specifically for this purpose. After setting the xy data coordinates, any six vertex closed polyline can be drawn with

Call p6_box(x1, y1, x2, y1, x2, y2, x3, y2, x3, y3, x1, y3)

 Sub p6_box(p1 As Double, p2 As Double, p3 As Double, p4 As Double, p5 As Double, p6 As Double, _ p7 As Double, p8 As Double, p9 As Double, p10 As Double, p11 As Double, p12 As Double) 

 Dim objent As AcadLWPolyline Dim pt(0 To 11) As Double pt(0) = p1: pt(1) = p2 pt(2) = p3: pt(3) = p4 pt(4) = p5: pt(5) = p6 pt(6) = p7: pt(7) = p8 pt(8) = p9: pt(9) = p10 pt(10) = p11: pt(11) = p12 Set objent = acadDoc.ModelSpace.AddLightWeightPolyline(pt) objent.Closed = True Set obj_Acad_Entity = objent End Sub

This makes no sense without a sketch but the sub p6_box can draw any closed polyline with 6 points configured any way you need it.

Our notched box is L X W with an A X B notch, drawn with the lower left corner at 0,0. There are 3 X coordinates and 3 Y coordinates.
X1=0 , X2=L-B , X3=L
Y1=0 , Y2=A , Y3=W

You can turn this box around any way you wish, move the notch to the middle, put a hole in the middle. Just label xy coordinates as needed in order from the origin. This is how you organize your xy data without duplication in a straightforward way. Sometimes its convenient to also label points, sometimes its not required, but the xy data must always be figured from the parameters as the first step.

In VBA we would probably draw in counterclockwise order.

Call p6_box(x1, y1, x2, y1, x2, y2, x3, y2, x3, y3, x1, y3)

Now it should make sense. The xydata starts at the origin. Subroutines can be written so declared point variables are not required, or required. If you have a lot of sub-routines, just declare your x1, x2, etc as public to avoid re-declaring.

In programming 101 they strongly suggest that your subroutines be simple and single purpose. Just about every autocad parametric program I have seen or written has been a mess at the actual geometry creation level. For instance in this example, the parameters A and B, L and W may need to have complicated formulas behind them. Put those upstream of the actual sub-routine that draws the geometry. Make the geometry creation as simple as possible. Pass the actual parameters if possible, do not develop them. Interface is top down thinking, but geometry is bottom up.
Such as

Sub draw_notch_box(W As Double, L As Double, A As Double, B As Double) x1 = 0 x2 = L - B x3 = L y1 = 0 y2 = A y3 = W Call p6_box(x1, y1, x2, y1, x2, y2, x3, y2, x3, y3, x1, y3) End Sub

You will be able to read that next year if you remember that xy data starts at the origin.

# Lisp Setup

To make pretty math graphs with lineweights, colors, background and good looking fonts with math symbols, we will need some basic setup tools.

Everybody has a lisp setup. This is the easy way, using the command function from lisp, but a DCL dialog cannot call the command function. It exits with error “unhandled exception.” Twas ever thus and evermore shall be.

(defun setup ()
(setq LT "continuous" LW "0.35")

(makelayer "Axis" 1 LT LW)
(makelayer "Directrix" 5 LT LW)
(makelayer "Focus" 5 LT LW)
(makelayer "Asymptote" 5 LT "Default")
(makelayer "Graph" 7 LT LW)

(makelayer "1" 1 LT LW)
(makelayer "2" 2 LT LW)
(makelayer "3" 3 LT LW)
(makelayer "4" 4 LT LW)
(makelayer "5" 5 LT LW)
(makelayer "6" 6 LT LW)
(makelayer "7" 7 LT LW)
(makelayer "8" 8 LT LW)
(makelayer "9" 9 LT LW)

(command "layer" "s" "graph" "")
(command "units" "2" "4" "1" "2" "0" "N" )
(command "setvar" "LWDISPLAY" 1)

(maketextstyle "Arial" "Arial")
(maketextstyle "Arial Narrow" "Arial Narrow")
(maketextstyle "Calibri" "Calibri")
(maketextstyle "Calibri Light" "Calibri Light")
(maketextstyle "Courier New" "Courier New")

(maketextstyle "Table1" "Arial Narrow")
(c:romans)
)

(defun makelayer (name color linetype lineweight )
(command "layer" "m" name "c" color "" "L" linetype "" "LW" lineweight "" "") )

(defun maketextstyle (name fontname )
(command "style" name fontname "0" "1.0" "0" "No" "No" ))

(defun c:romans()
(command "style" "RomanS" "romans" "0" "0.75" "0" "No" "No" "No"))

(defun bkg (rgbnum)
(setq hexnum (rgbhex rgbnum))

(defun RGBhex (RGBnum / r g b)
(setq r (lsh RGBnum 16))
(setq g (lsh RGBnum 8))
(setq b RGBnum)
(+ (+ r g) b)  )



to run code above from a DCL dialog we need to replace the command functions with entmake or visual lisp activeX methods.

autolisp layer entmake search yields many results.

for reference get the dxf codes returned for objects –

Command: (entget (tblobjname “layer” “0”))
((-1 . )
(0 . “LAYER”)
(5 . “10”)
(360 . )
(102 . “}”)
(330 . )
(100 . “AcDbSymbolTableRecord”)
(100 . “AcDbLayerTableRecord”)
(2 . “0”)
(70 . 0)
(62 . 7)
(6 . “Continuous”)
(290 . 1)
(370 . -3)
(390 . )
(347 . )
(348 . ))

Command: (entget (tblobjname “style” “arial”))
((-1 . )
(0 . “STYLE”)
(330 . )
(5 . “21B”)
(100 . “AcDbSymbolTableRecord”)
(100 . “AcDbTextStyleTableRecord”)
(2 . “Arial”)
(70 . 0)
(40 . 0.0)
(41 . 1.0)
(50 . 0.0)
(71 . 0)
(42 . 0.2)
(3 . “Arial”)
(4 . “”))

makelayer and maketextstyle are remade with the same argument list –

(DEFUN makelayer (name color Linetype Lineweight)
(entmake (list (cons 0 “LAYER”)
(cons 100 “AcDbSymbolTableRecord”)
(cons 100 “AcDbLayerTableRecord”)
(cons 2 name)
(cons 70 0)
(cons 62 color)
(cons 6 Linetype)
(cons 290 1)
(cons 370 Lineweight))))

(DEFUN maketextstyle (name fontname)
(entmake (list (cons 0 “STYLE”)
(cons 100 “AcDbSymbolTableRecord”)
(cons 100 “AcDbTextStyleTableRecord”)
(cons 2 name)
(cons 3 fontname)
(cons 70 0)
(cons 40 0.0)
(cons 41 1.0)
(cons 50 0.0)
(cons 71 0))))

autocad will let us know if DXF does not like the structure of the DXF code. We have to find the actual font name and suffix the extension.

(maketextstyle “Arial” “Arial.ttf”)
(maketextstyle “Arial Narrow” “ArialN.ttf”)
(maketextstyle “Calibri” “Calibri.ttf”)
(maketextstyle “Calibri Light” “CalibriL.ttf”)
(maketextstyle “Courier New” “Cour.ttf”)
(maketextstyle “Symbol” “Symbol.ttf”)

similarly the lineweight must be changed from 0.35 to 35, there may be other minor differences, such as the value for “Default” that has to be figured out. query the actual object in the drawing with the entget function above.

Fonts consist of 256 characters. a table 16 X 16 can display them all. Addtable works closely with the Tablestyle creation program. I have a statement to load Make_Ts at the head of the FontTable program. The tablestyle program uses the current textstyle and names itself the same way. to make a font table with a different font, i change the current textstyle, then re-run fonttable. This is a good reference for viewing the entire table for symbols and also i expect it to be handy for cut and paste to find greek symbols.

The original verson of the lisp tablestyle creation was taken from Lee Ambrosius’ blog
http://hyperpics.blogs.com/beyond_the_ui/2012/07/creating-a-table-style-with-autolisp-and-the-activex-api.html

(vl-load-com)

(defun c:Fonttable()

(make_ts)

(setq numrows 17 numcolumns 16 rowheight 0.25 colwidth 0.375)

(setq pt (vlax-3d-point 0 0 0))

(setq modelSpace (vla-get-ModelSpace doc))
(setq fTable (vla-Addtable modelSpace pt numrows numcolumns rowheight colwidth))

(vla-settext fTable 0 0 (getvar "textstyle"))

(setq rownum 1 colnum 0 chrnum 0)

(repeat 16

(repeat 16
(vla-settext fTable rownum colnum (chr chrnum))
(setq colnum (1+ colnum))
(setq chrnum (1+ chrnum)))

(setq rownum (1+ rownum))
(setq colnum 0)
)
)

(defun Make_TS()
;original version from
;;http://hyperpics.blogs.com/beyond_the_ui/2012/07/creating-a-table-style-with-autolisp-and-the-activex-api.html
; some of his code i changed i commented out as this file changes a lot depending on specific requirements
; and it is helpful to keep alternate methods and settings available.

;tablestyle uses and is named by current textstyle
(setq textstyle (getvar "textstyle"))

;; Get the AutoCAD application and current document

;; Get the Dictionaries collection and the TableStyle dictionary
(setq dicts (vla-get-Dictionaries doc))

;; Create a custom table style
(setq key textstyle
class "AcDbTableStyle")
(setq custObj (vla-AddObject dictObj key class))

;; Set the name and description for the style
(vla-put-Name custObj textstyle)
(vla-put-Description custObj "Font named table style")

;; Sets the bit flag value for the style
(vla-put-BitFlags custObj 1)

;; Sets the direction of the table, top to bottom or bottom to top
(vla-put-FlowDirection custObj acTableTopToBottom)

;; Sets the supression of the table header
;; Does not seem to do anything in tablestyle
;; same statement using created table object must be in addtable

;; Sets the horizontal margin for the table cells
(vla-put-HorzCellMargin custObj 0.03)

;; Sets the supression of the table title
(vla-put-TitleSuppressed custObj :vlax-false)

;; Sets the vertical margin for the table cells
(vla-put-VertCellMargin custObj 0.03)

;; Set the alignment for the Data, Header, and Title rows
;; (vla-SetAlignment custObj (+ acDataRow acTitleRow) acMiddleLeft)
(vla-SetAlignment custObj acDataRow acMiddlecenter)
(vla-SetAlignment custObj acTitleRow acMiddleCenter)

;; Set the background color for the Header and Title rows

;(vla-SetRGB colObj 98 136 213)
;(vla-SetBackgroundColor custObj (+ acHeaderRow acTitleRow) colObj)

;; Clear the background color for the Data rows
(vla-SetBackgroundColorNone custObj acDataRow :vlax-true)

;; Set the bottom grid color for the Title row
;;63 is all gridlinetypes and 7 is all rowtypes
(vla-SetRGB colObj 0 0 255)
(vla-SetGridColor custObj 63 7 colObj)

;;     to set individual - bottom grid color for the Title row
;;    (vla-SetGridColor custObj acHorzBottom acTitleRow colObj)

;; Set the bottom grid lineweight for the Title row
(vla-SetGridLineWeight custobj acHorzBottom acTitleRow acLnWt025)

;; Set the inside grid lines visible for the data and header rows
(vla-SetGridVisibility custObj acHorzInside  (+ acDataRow acHeaderRow) :vlax-true)

;; Set the text height for the Title, Header and Data rows
(vla-SetTextHeight custObj acTitleRow 0.25)
(vla-SetTextHeight custObj acDataRow 0.125)

;; Set the text style
;;(vla-SetTextStyle custObj (+ acDataRow acHeaderRow acTitleRow) "Standard")
(vla-SetTextStyle custObj (+ acDataRow acHeaderRow acTitleRow) (getvar "textstyle"))

;; Release the color object
(vlax-release-object colObj)
(setvar "ctablestyle" textstyle)

(princ)
)



# Curve Catalog

AutoLisp DCL Curve Graphing with curve “recipes” – equation, x value ranges, x value increment – saved and retrieved in an excel CSV file. Curve parameters in an excel list can be edited, sorted and re-organized. A sub-form Curve Catalog is popped up, data filled from the CSV file. User selects curve. Parameters are input to the Graph screen.

The code to Read the Excel.CSV file was written by Lee Mac and downloaded from his premier lisp site.

Graphing mathematical equations with autolisp –
as an example,
(setq strfunc “(* a x x )”)
(setq a 2) returns 2
(setq x 2.1) returns 2.1
Strfunc returns “(* a x x )”
(read strfunc) returns (* A X X)

With this we can set up a function to return a result for any legal lisp statement and values x a b c d.
The function is passed as a string, strfunc. X is passed in as a Real. X will vary with each call from xmin to xmax by increment xinc,
The global vars for a_dim b_dim c_dim and d_dim do not vary.

(defun func_eval (x strfunc / a b c d)
(setq a a_dim b b_dim c c_dim d d_dim )
(setq result (eval (read strfunc))) )

The equations must be input in lisp form, prefix notation with the operator first in a list, expressions nested. As long as it is legal lisp, the above code will interpret it. The loop to draw the curve is basic lisp using the command function for either line or pline.

;; location of
;; graph.lsp and dcl
;; catalog.lsp and dcl
;; curve_catalog.csv

(setq graph_progdir "c:\\lisp\\graph\\")

(defun c:graph ()
;catalog sub form program

(setq dcl_id (load_dialog (strcat graph_progdir "graph.dcl")))
(if (< dcl_id 0)
(progn
(exit) ) )

(if (not (new_dialog "graph" dcl_id))
(progn
(alert "DCL file loaded but not definition, internal problem with files" )
(exit) )   )

(set_graph_vars)
(action_tile "catalog" "(catalog) (set_graph_vars)")
(action_tile "graphlines" "(savevars) (done_dialog 2)")
(action_tile "graphpolylines" "(savevars) (done_dialog 3)")
(action_tile "cancel" "(done_dialog 0)")

(setq ddiag (start_dialog))
(if (= ddiag 2) (graphlines) )
(if (= ddiag 3) (graphpolylines) )
)

(defun savevars	()
(setq xmin (atof (get_tile "xmin")))
(setq xmax (atof (get_tile "xmax")))
(setq xinc (atof (get_tile "xinc")))
(setq a_dim (atof (get_tile "a_dim")))
(setq b_dim (atof (get_tile "b_dim")))
(setq c_dim (atof (get_tile "c_dim")))
(setq d_dim (atof (get_tile "d_dim")))
(setq strfunc (get_tile "equation"))
)

(defun set_graph_vars ()
(if xmin
(set_tile "xmin" (rtos xmin 2 2))
(set_tile "xmin" "-3.0")  )

(if xmax
(set_tile "xmax" (rtos xmax 2 2))
(set_tile "xmax" "3.0")  )

(if xinc
(set_tile "xinc" (rtos xinc 2 2))
(set_tile "xinc" "0.1")  )

(if a_dim
(set_tile "a_dim" (rtos a_dim 2 2))
(set_tile "a_dim" "2")  )

(if b_dim
(set_tile "b_dim" (rtos b_dim 2 2))
(set_tile "b_dim" "3")  )

(if c_dim
(set_tile "c_dim" (rtos c_dim 2 2))
(set_tile "c_dim" "4")  )

(if d_dim
(set_tile "d_dim" (rtos d_dim 2 2))
(set_tile "d_dim" "5")  )

;initial equation ax^2 + bx + c
(if (null strfunc)
(set_tile "equation" "(+ (* a X X) (* b X) c)")
(set_tile "equation" strfunc)  )
)

(defun func_eval (x strfunc / a b c d)
(setq	a a_dim    b b_dim   c c_dim   d d_dim  )
(setq result (eval (read strfunc))) )

(defun graphlines ()	;working with all global variables from savevars

(setq numlines (/ (- xmax xmin) xinc))
(setq i 1)
(setq x1 xmin)
(setq y1 (func_eval x1 strfunc))

(repeat (fix numlines)

(setq x2 (+ xmin (* i xinc)))
(setq y2 (func_eval x2 strfunc))

(setq pt1 (list x1 y1))
(setq pt2 (list x2 y2))

(command "line" pt1 pt2)
(command)

(setq x1 x2)
(setq y1 y2)

(setq i (1+ i))
)
)

(defun graphpolylines ()    ;working with all global variables from savevars

(setq numlines (/ (- xmax xmin) xinc))
(setq x1 xmin)
(setq y1 (func_eval x1 strfunc))
(setq pt1 (list x1 y1))

(command "pline" pt1)

(setq i 1)
(repeat (fix numlines)

(setq x2 (+ xmin (* i xinc)))
(setq y2 (func_eval x2 strfunc))

(setq pt2 (list x2 y2))

(command pt2)
(setq i (1+ i))
)
(command)
)



Graph.DCL is straightforward

Each curve then is defined by its lisp string, xmin, xmax, xinc and the specific values of A B C and D if they are used. Those 8 variables can be saved in an Excel file, a list of curve recipes, and input to the Graph program through a nested dialog called Catalog. A couple extra information columns are added.

The Excel format is called CSV, Comma Separated Values. It is actually a text file. It can be loaded directly into a text editor. Autolisp has tools to read this format. Excel is very handy for displaying and managing it. Lee Mac’s program reads the table and returns a list of rows with each cell a separate item in the sub-list. With Lisp tools we can take that and re-display the CSV file in DCL list boxes.

I only make one list box active to user selection, the box with the legal lisp equation. the other boxes are for user reference. all list boxes are given the same height and handled the same way so the rows line up. When the user selects an equation, its parameters are written to the textboxes, however he has to select OK to dismiss the dialog and use the parameters. Selecting cancel will abandon the values and return the previous screen with the values unchanged.

Here is Catalog LSP, called from the button on Graph. much of its length is just from loading the 10 listboxes and dealing with variables.

;catalog_lsp called from graph_lsp

(defun catalog_setup ()
(setq file (strcat graph_progdir "curve_catalog.csv"))

;;*********************************************
;;*********************************************

(setq theList1  (mapcar 'car data))

(setq theList4  (mapcar '(lambda (abc) (nth 3 abc)) data))
(setq theList5  (mapcar '(lambda (abc) (nth 4 abc)) data))
(setq theList6  (mapcar '(lambda (abc) (nth 5 abc)) data))
(setq theList7  (mapcar '(lambda (abc) (nth 6 abc)) data))
(setq theList8  (mapcar '(lambda (abc) (nth 7 abc)) data))
(setq theList9  (mapcar '(lambda (abc) (nth 8 abc)) data))
(setq theList10  (mapcar '(lambda (abc) (nth 9 abc)) data))
)

(defun catalog ()
(catalog_setup)

(setq dcl_id (load_dialog (strcat graph_progdir "catalog.dcl")))
(if (< dcl_id 0)
(exit) ) )

(if (not (new_dialog "catalog" dcl_id))
(progn (alert "DCL file loaded but not definition, internal problem with files")
(exit) ) )

(set_catalog_vars)

(start_list "list1" 3)
(end_list)

(start_list "list2" 3)
(end_list)

(start_list "list3" 3)
(end_list)

(start_list "list4" 3)
(end_list)

(start_list "list5" 3)
(end_list)

(start_list "list6" 3)
(end_list)

(start_list "list7" 3)
(end_list)

(start_list "list8" 3)
(end_list)

(start_list "list9" 3)
(end_list)

(start_list "list10" 3)
(end_list)

;only list that is pickable is lisp equation
(action_tile "list3" "(list-pick)")

(action_tile "ok" "(savevars) (done_dialog 2)")
(action_tile "cancel" "(done_dialog 0)")

(setq ddiag (start_dialog))

;(if (= ddiag 2) (nothing) )
;not required - savevars in ok button writes vars
;catalog button in graph lsp reads vars to screen
)

(defun savevars	()
(setq xmin (atof (get_tile "xmin")))
(setq xmax (atof (get_tile "xmax")))
(setq xinc (atof (get_tile "xinc")))

(setq a_dim (atof (get_tile "a_dim")))
(setq b_dim (atof (get_tile "b_dim")))
(setq c_dim (atof (get_tile "c_dim")))
(setq d_dim (atof (get_tile "d_dim")))

(setq strfunc (get_tile "equation"))
)

(defun list-pick ()
(setq listval (get_tile "list3"))
(setq curve_recipe (nth (atoi listval) data))

(set_tile "equation" (nth 2 curve_recipe))
(set_tile "xmin" (nth 3 curve_recipe))
(set_tile "xmax" (nth 4 curve_recipe))
(set_tile "xinc" (nth 5 curve_recipe))
(set_tile "a_dim" (nth 6 curve_recipe))
(set_tile "b_dim" (nth 7 curve_recipe))
(set_tile "c_dim" (nth 8 curve_recipe))
(set_tile "d_dim" (nth 9 curve_recipe))
)

;code same as set_graph_vars, keeping them separate for now
(defun set_catalog_vars ()
(if xmin
(set_tile "xmin" (rtos xmin 2 2))
(set_tile "xmin" "-3.0") )

(if xmax
(set_tile "xmax" (rtos xmax 2 2))
(set_tile "xmax" "3.0") )

(if xinc
(set_tile "xinc" (rtos xinc 2 2))
(set_tile "xinc" "0.1") )

(if a_dim
(set_tile "a_dim" (rtos a_dim 2 2))
(set_tile "a_dim" "2") )

(if b_dim
(set_tile "b_dim" (rtos b_dim 2 2))
(set_tile "b_dim" "3") )

(if c_dim
(set_tile "c_dim" (rtos c_dim 2 2))
(set_tile "c_dim" "4") )

(if d_dim
(set_tile "d_dim" (rtos d_dim 2 2))
(set_tile "d_dim" "5") )

;initial equation ax^2 + bx + c
(if (null strfunc)
(set_tile "equation" "(+ (* a X X) (* b X) c)")
(set_tile "equation" strfunc) )
)



and last, the DCL file for Catalog, again much of its length is repeating operations for the many listboxes


catalog:dialog {
label = "Curve Catalog";

:row {
:list_box {
label ="Notes";
key = "list1";
height = 35;
width = 35;
multiple_select = false;
fixed_width_font = true;
value = "0"; }

:list_box {
label ="Description";
key = "list2";
height = 35;
width = 35;
multiple_select = false;
fixed_width_font = true;
value = "0"; }

:list_box {
label ="Function - *** PICK FROM THIS LIST ***";
key = "list3";
height = 35;
width = 50;
multiple_select = false;
fixed_width_font = true;
value = "0"; }

:list_box {
label ="Xmin";
key = "list4";
height = 35;
width = 7;
multiple_select = false;
fixed_width_font = true;
value = "0"; }

:list_box {
label ="Xmax";
key = "list5";
height = 35;
width = 7;
multiple_select = false;
fixed_width_font = true;
value = "0"; }

:list_box {
label ="Xinc";
key = "list6";
height = 35;
width = 7;
multiple_select = false;
fixed_width_font = true;
value = "0"; }

:list_box {
label ="A_Dim";
key = "list7";
height = 35;
width = 7;
multiple_select = false;
fixed_width_font = true;
value = "0"; }

:list_box {
label ="B_Dim";
key = "list8";
height = 35;
width = 7;
multiple_select = false;
fixed_width_font = true;
value = "0"; }

:list_box {
label ="C_Dim";
key = "list9";
height = 35;
width = 7;
multiple_select = false;
fixed_width_font = true;
value = "0"; }

:list_box {
label ="D_Dim";
key = "list10";
height = 35;
width = 7;
multiple_select = false;
fixed_width_font = true;
value = "0"; }
}

:row {
:edit_box {
label = "Xmin";
key = "xmin";}
:edit_box {
label = "Xmax";
key = "xmax";}
:edit_box {
label = "Xinc";
key = "xinc";}

:edit_box {
label = "A_dim";
key = "a_dim";}
:edit_box {
label = "B_dim";
key = "b_dim";}
:edit_box {
label = "C_dim";
key = "c_dim";}
:edit_box {
label = "D_dim";
key = "d_dim";}
}

:row {
:edit_box {
label = "Y = ";
key = "equation";}
}

:row {
: button {
label = "OK";
key = "ok";
is_default = true; }

: button {
label = "Cancel";
key = "cancel";
is_default = false;
is_cancel = true; }
}

}