# 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.
http://www.lee-mac.com/readcsv.html  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)
(eval (read strfunc)) returns 8.82

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
(load (strcat graph_progdir "catalog.lsp"))

(setq dcl_id (load_dialog (strcat graph_progdir "graph.dcl")))
(if (< dcl_id 0)
(progn
(alert "The graph.DCL file could not be loaded.")
(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))
(unload_dialog dcl_id)
(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"))

;;*********************************************
;; Read CSV code from
;;http://www.lee-mac.com/readcsv.html
;;*********************************************
(setq data (LM:readcsv file))

(setq theList1  (mapcar 'car data))
(setq theList2  (mapcar 'cadr data))
(setq theList3  (mapcar 'caddr 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)
(progn  (alert "The catalog.DCL file could not be loaded.")
(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)
(mapcar 'add_list theList1)
(end_list)

(start_list "list2" 3)
(mapcar 'add_list theList2)
(end_list)

(start_list "list3" 3)
(mapcar 'add_list theList3)
(end_list)

(start_list "list4" 3)
(mapcar 'add_list theList4)
(end_list)

(start_list "list5" 3)
(mapcar 'add_list theList5)
(end_list)

(start_list "list6" 3)
(mapcar 'add_list theList6)
(end_list)

(start_list "list7" 3)
(mapcar 'add_list theList7)
(end_list)

(start_list "list8" 3)
(mapcar 'add_list theList8)
(end_list)

(start_list "list9" 3)
(mapcar 'add_list theList9)
(end_list)

(start_list "list10" 3)
(mapcar 'add_list theList10)
(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))
(unload_dialog dcl_id)

;(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; }
}

}
```
Advertisements

# AutoLisp DCL Equation Graphing

here is a start. equations have to be input in Lisp format. prototype, barely tested beyond initial ah ha!  Both line and polyline mode are enabled. The program remembers variables from run to run, so if you input a new equation, it will come up on next run.

```

(defun c:graph ()

(setq dcl_id (load_dialog "c:\\lisp\\graph\\graph.dcl"))
(if (< dcl_id 0)
(progn
(alert "The graph.DCL file could not be loaded.")
(exit) ) )

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

(setgraphvars)

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

(setq ddiag (start_dialog))

(unload_dialog dcl_id)

(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 str_equation (get_tile "equation"))
)

(defun setgraphvars ()
(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 str_equation)
(set_tile "equation" "(+ (* a X X) (* b X) c)")
(set_tile "equation" str_equation) ) )

(defun resetgraph ()
(set_tile "xmin" "-3.0")
(set_tile "xmax" "3.0")
(set_tile "xinc" "0.1")

(set_tile "a_dim" "2")
(set_tile "b_dim" "3")
(set_tile "c_dim" "4")
(set_tile "d_dim" "5")

(setq str_equation "(+ (* a X X) (* b X) c)") ;initial equation
(set_tile "equation" str_equation)	; ax^2 + bx + c
)

(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 strfunc str_equation)	; could clean this up

(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 strfunc str_equation)	; eval read strfunc

(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)

)
```

Graphs below are cos, sin and cos * sin
(cos X) White
(sin X) Red
(* (sin X) (cos X)) Blue # Autocad DCL Dialog Forms

Dialog Control Language – DCL – was introduced in its final form 25 years ago. If you have created forms with VBA, DCL has a tiny fraction of the capability. It uses archaic unfamiliar terms and it is evidently hard to describe as no one does it well. My advice is to find a template and create some working code as you try to read about it. The best template and teaching approach I have used is Jeffery Sanders website. He has a good basic template at the bottom of the very first page.

JefferySanders AutoLisp DCL Tutorial

There are two components – the DCL file and the Lisp file. The DCL file looks confusing at first but its not. The basic element is the Tile, in VBA that is called a Control. There are only a few Tiles that the user actually uses – textbox, button, listbox, popup list, radio button and toggle. There are several other column and row spacings which are technically tiles but you can leave them out of your first programs. Each Tile has attributes, in VBA called Properties. The most important attribute is called a Key, in VBA that is just the Name. The structure of tile and attributes is not hard to learn if you just start typing. Develop your own style of formatting and indenting. As you add rows and column objects, indent the Tiles so the Colon for each level is in a vertical line on the page.

Here is my style that i can read at a glance without counting brackets. The visual lisp IDE has a DCL preview tool that is essential to get working. It is broken by default (or perhaps neglect). AfraLISP has clear instructions here to change permissions on your autocad installation folder.

AfraLisp Previewing DCL Files

Autodesk help has a LSP-DCL example overview.

Example: Quick Overview of Dialog Boxes (DCL)

DCL

```hello  : dialog {
label = "sample box";
: text { label = "Hello world"; }
ok_only;
}
```

LSP

```(defun C:HELLO ( / dcl_id )
(setq dcl_id (load_dialog "hello.dcl")) ; Load the DCL file.
(if (not (new_dialog "hello" dcl_id))   ; Initialize the dialog.
(exit)                                ; Exit if this does not work.
)
(start_dialog)                          ; Display the dialog box.
(unload_dialog dcl_id)                  ; Unload the DCL file.
(princ)
)
```

Running this quickly shows it runs, displays and closes but it does not do anything. There is no input from the user. The mystery of these files remains – how is user input processed when they close?

start-dialog is running when the dialog is displayed, when the dialog is closed, start-dialog returns a status number. if the builtin ok_cancel buttons are used in the dcl file, the numbers are 0 for cancel and 1 for ok. that is what is happening behind the scenes in the program above, though there is no ok button.

You can start modifying the above program by adding an ok button, and capture the integer returned by start_dialog as it closes the dialog. The built-in ok_cancel buttons have built-in keys of “accept” and “cancel.” With that knowledge you can build an action_tile statement in your lisp program that is run when that button is pushed. Eventually we need to run our own program when the ok button is pushed.

(action_tile “accept” “(done_dialog 1)”) will cause the start_dialog exit and return the number 1 when the ok button is pushed. that happens to be the same number as the default, you could choose any number and start_dialog will return that number. The string of lisp commands is quoted so it does not evaluate when first loaded. This is why you just have to start running code because its so hard to explain. This area of how it closes and how user input is captured is the key part that is hardest to get from the manuals.

To make this overview program do something user related lets take this intermediate step where we add an edit_box, an ok_cancel assembly and examine the start_dialog return values. Adding some error checking to the dcl loading is straightforward.

DCL

```hello2  : dialog {
label = "hello2";

: edit_box {
label = "type in the box";
key = "hellobox";
value = "hello world";
width = 50; }

ok_cancel;
}
```

LSP

```(defun c:hello2 ( / dcl_id)

;'can the file be found and loaded
;'load_dialog returns positive integer when successful
;'returns a negative integer when not successful

(setq dcl_id (load_dialog "c:\\lisp\\dcl\\hello.dcl"))
(if (< dcl_id 0)
(progn
(alert "The Hello.DCL file could not be loaded.")
(alert "Check DCL file location is in Search Path")
(exit)))

;'(alert (rtos dcl_id))

;'new_dialog returns T or nil
(if (not (new_dialog "hello2" dcl_id))
(progn
(alert "DCL file loaded but not definition, internal problem with files")
;'this might even be inconsistent capitalization of the dialog name
(exit)))

;'as an experiment uncomment these lines
;'these numbers are returned by start_dialog
;'they mean whatever you want them to mean - they overwrite default 0 and 1
;'accept and cancel are the built-in key names you need to know for the ok_cancel sub-assembly
;'(action_tile "accept" "(done_dialog 3)")
;'(action_tile "cancel" "(done_dialog 12)")

;'by default start_dialog returns 0 if cancel, 1 if OK, -1 if all boxes term_dialog
;'alternatively, if done_dialog argument is an integer > 1
;'then start_dialog returns that value which is user-application defined

(setq d (start_dialog))
(unload_dialog dcl_id)

(alert (itoa d))
)
```

now maybe we have some understanding of how the built-in buttons pass values behind the scenes to the start-dialog, we can do the same thing with explicitly created buttons in the dcl. we don’t have to do that, but perhaps it is simpler to see how it works. The last most important piece of the puzzle, where and how do we save user input? i take this technique from the jefferysanders template above, create a (savevars) function and put it in the action_tile statement for the ok button. Test the number returned by start_dialog after exiting the dialog to see if it gets used. now the helloworld demo file returns whatever the user types in to the box.

DCL

```hello3  : dialog {
label = "hello3";

: edit_box {
label = "type in the box";
key = "hellobox";
value = "hello world";
width = 50; }

: row {

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

```

LSP

```(defun c:hello3 ( / dcl_id)

;'can the file be found and loaded
;'load_dialog returns positive integer when successful
;'returns a negative integer when not successful

(setq dcl_id (load_dialog "c:\\lisp\\dcl\\hello.dcl"))
(if (< dcl_id 0)
(progn
(alert "The Hello.DCL file could not be loaded.")
(alert "Check DCL file location is in Search Path")
(exit)))

;'new_dialog returns T or nil
(if (not (new_dialog "hello3" dcl_id))
(progn
(alert "DCL file loaded but not definition, internal problem with files")
;'this might even be inconsistent capitalization of the dialog name
(exit)))

;'now using explicitly created button tiles rather than built-in
;'but it is not necessary, same results are with hello2.dcl
(action_tile "cancel" "(done_dialog 0)")
(action_tile "accept" "(savevars) (done_dialog 1)")

;'by default start_dialog returns 0 if cancel, 1 if OK, -1 if all boxes term_dialog
(setq d (start_dialog))

(unload_dialog dcl_id)

;'pop up contents of edit_box if ok is pushed
(if (= d 1) (alert textval))
)

(defun savevars ()
(setq textval (get_tile "hellobox")))

```