If you're already an experienced ME Macro programmer who understands the basics of:
... you won't have too much trouble learning to do the same things in Lisp. Lisp is a much bigger, richer language than the ME Macro language, but the basics are similar. This document will help you get started by describing a subset of Lisp commands that you can use when writing programs for Creo Elements/Direct Modeling. It is by no means a comprehensive description of Lisp. In fact, it deliberately neglects some details in order to keep things simple. The intention is to get you started writing Lisp programs without getting overwhelmed by the shear number of commands available.
You'll definitely want to get a Lisp reference book for more detailed information. Hopefully, you'll find that Lisp is pretty easy to learn if you start writing some simple programs using the basics described here, and then add new commands to your repetoire by referring to the books and trying more complex examples.
There are plenty of other books about Lisp available, as well as FAQ lists on the Internet. These will get you started, though.
Lisp is an interpreted language, just like the ME Macro Language. That means you can develop your programs interactively without having to compile them. It's possible to enter/load and run your programs directly from the Creo Elements/Direct Modeling command line. You can enter any Lisp command at the Creo Elements/Direct Modeling prompt "Enter Command".
On the Creo Elements/Direct Modeling command line, you can retrieve previous inputs and edit them. To do this, use:
To move the cursor for editing, use the left and right arrows keys.
You can also write your experimental code in a file and load it into Creo Elements/Direct Modeling:
(load "/tmp/myfile.lsp")
By convention, Lisp source files have names ending in ".lsp".
Any output generated by your Lisp commands will be written to the terminal window from which you started Creo Elements/Direct Modeling.
Let's take a look at a Lisp expression to get a feeling for the syntax:
(+ 2 2) 4
Notice three things:
All expressions in Lisp are surrounded by parentheses. You'll see lots of them. At first, you may go blind trying to match them up. Don't worry, text editors like vi and emacs have built-in functions for matching parentheses.
Also, all function calls, from the simplest mathematical operation, to the most complex function that you write yourself, are made with this prefix notation, where the function comes first followed by its parameters:
(my-addition-function 2 2) 5
Unlike the Macro language, Lisp functions always return a value. This is true of built-in functions as well as the functions that you write yourself.
It's also important to note that Lisp is not case-sensitive:
(my-addition-function 2 2) 5 (My-Addition-Function 2 2) 5 (mY-aDDITION-fUNCTION 2 2) 5
However, variables and function names (called symbols) are typically all lower-case. By convention, individual words in these names are separated by a dash - rather than an underscore _.
You'll want to include comments in your programs. You can do this two ways:
; This function doesn't seem to be working... #| (my-addition-function 2 3) (my-addition-function 6 2) (my-addition-function 3 7) |# ;; Use standard operator instead (+ 2 3) (+ 6 2) (+ 3 7)
In the Macro language, you could define a global variable outside one of your macros and give it a value using a LET statement. You can do the same in Lisp using defvar:
LET My_global_variable "foo" (defvar *my-global-variable* "foo")
The initial value of the global variable *my-global-variable* can be set when it is defined as shown. Suppose you want to change its value later? In the Macro language, you would just use LET again. In Lisp, you need to use setf:
LET My_global_variable "bar" (setf *my-global-variable* "bar")
Another call to defvar wouldn't work. Multiple calls to defvar with the same variable name and different values will only set the value the first time; subsequent calls will not change the value once it is already set.
Note: The assignment was done using setf. There are other ways to assign values in Lisp (like setq), but setf is the most general and will always work.
To define a constant whose value can not be changed after definition, use defconstant:
(defconstant *my-global-constant* "always foo") (setf *my-global-constant* "bar") Error: Cannot assign to the constant *MY-GLOBAL-CONSTANT*.
By convention, the names of global variables and constants begin and end with an asterix *.
Just like in the Macro language, variables don't have a particular data type. The same variable can represent an integer, a real number, a string, or any other data type.
(setf any-type (+ 2 2)) (setf any-type 3.141592654) (setf any-type "a string")
You can determine the type of value that a variable is assigned using the type-of command:
(setf any-type (+ 2 2)) 4 (type-of any-type) FIXNUM (setf any-type 3.141592654) 3.1415926540000001 (type-of any-type) LONG-FLOAT (setf any-type "a string") "a string" (type-of any-type) STRING
Lisp offers the operators you'll need for making arithmetic calculations:
(+ 1.5 2.3 3.45) 7.25 (- 4 2) 2 (* 2 3 7.4) 44.400000000000006 (/ 6 -2) -3 (sqrt 4) 2.0
Lisp includes boolean operators for comparing numbers as well. These boolean comparisons return either:
The numerical comparison functions are:
(= 3 3) T (= 6 (* 3 2) (/ 12 2)) T (= 3 4 5) NIL (/= 2 5 3 6) T (/= 2 5 2) NIL (< 2 3) T (< 2 4 1) NIL (< 2 2 3) NIL (<= 2 2 3 4) T (> 4 3 2) T (> 4 3) NIL (> 4 4 3) NIL (>= 4 4 3) T (numberp 2) T (numberp (+ 3.34 2.34)) T (numberp "2") NIL
The book by Steele gives the definitive list of operators available in Lisp. The Yuasa book is also pretty complete on this topic.
There will also be special arithmetic functions defined for Creo Elements/Direct Modeling to operate on geometric elements like points and vectors. Those functions will be covered in Creo Elements/Direct Modeling documents.
Lisp also has many functions for manipulating strings.
You can inquire properties of a single string:
(stringp "string") T (stringp 5) NIL (length "sample string") 13 (subseq "some string" 0) "some string" (subseq "some string" 1) "ome string" (subseq "some string" 0 4) "some"
You can compare two strings:
(string= "string1" "string1") T (string= "string1" "string2") NIL (string< "string1" "string2") 6 (string< "string2" "string1") NIL (string< "string1" "string1") NIL
You can build a string:
(write-to-string 5) "5" (write-to-string (+ 2 2)) "4" (format nil "~a and ~a is ~a" 2 2 (+ 2 2)) "2 and 2 is 4" (format nil "~a~%and~%~a~%is~%~a" 2 2 (+ 2 2)) "2 and 2 is 4"
Note: Creo Elements/Direct Modeling also provides some string handling functions to deal with localizable strings. Those functions will be covered in Creo Elements/Direct Modeling documents.
Lists are the fundamental data type of Lisp. You can make lists out of all sorts of Lisp objects and assign them to variables:
(setf integers-list (list 1 2 3 4)) (1 2 3 4) (setf sums-list (list (+ 1 1) (+ 2 2) (+ 3 3))) (2 4 6) (setf strings-list (list "list" "of" "some" "strings")) ("list" "of" "some" "strings") (setf integers-and-strings-list (list 1 "two" 3 "four")) (1 "two" 3 "four")
The function list explicitly creates a list out of the values and/or the results of any expressions that you give to it. You can also create a list using a quote ':
(setf sum-expressions-list '((+ 1 1) (+ 2 2) (+ 3 3))) ((+ 1 1) (+ 2 2) (+ 3 3))
The quote ' prevents any of the included expressions from being evaluated. If you want some of them to be evaluated, use a backquote ` and commas , in combination:
(setf sums-and-expressions-list `((+ 1 1) ,(+ 2 2) (+ 3 3) ,(+ 4 4))) ((+ 1 1) 4 (+ 3 3) 8)
Using this technique, the expressions preceded by a comma are evaluated and the others are not. "So what?", you might well be thinking. Eventually, you may find powerful uses for this mixture of immediate and delayed evaluation. For now, it's just important to understand the meaning when you read example programs that use this technique.
You can find out how many items are in a list:
(length '(1 2 3 4 5 6)) 6 (length '()) 0
You can check a list to see if it includes a certain item:
(member 1 '(1 2 3)) (1 2 3) (member 2 '(1 2 3)) (2 3) (member 4 '(1 2 3)) NIL
You can extract values from lists:
(setf example-list '("zero" 1 "two" 3 "four" 5)) ("zero" 1 "two" 3 "four" 5) (first example-list) "zero" (rest example-list) (1 "two" 3 "four" 5) (last example-list) (5) (butlast example-list) ("zero" 1 "two" 3 "four") (nth 2 example-list) "two" (nth 10 example-list) NIL (first (rest example-list)) 1 (rest (last example-list)) NIL
You can also add items to lists:
(setf original-list '(1 2 3 4)) (1 2 3 4) (setf new-list (cons 0 original-list)) (0 1 2 3 4) new-list (0 1 2 3 4) original-list (1 2 3 4) (push 0 original-list) (0 1 2 3 4) original-list (0 1 2 3 4) (setf newlist (append original-list '(6 7 8))) (0 1 2 3 4 6 7 8) newlist (0 1 2 3 4 6 7 8) original-list (0 1 2 3 4) (nconc original-list '(6 7 8))) (0 1 2 3 4 6 7 8) original-list (0 1 2 3 4 6 7 8)
There are functions to remove items from lists:
original-list (0 1 2 3 4 6 7 8) (pop original-list) 0 original-list (1 2 3 4 6 7 8) (delete 4 original-list) (1 2 3 6 7 8) original-list (1 2 3 6 7 8) (remove 3 original-list) (1 2 6 7 8) original-list (1 2 3 6 7 8)
Lisp includes other useful data types in addition to lists. One particularly useful type is the structure:
Structures are defined using the defstruct command:
(defstruct 3d_point (x 0) (y 0) (z 0)) 3D_POINT (setf origin (make-3d_point)) #S(3D_POINT X 0 Y 0 Z 0) (setf some-3d-point (make-3d_point :x 1 :y 2 :z 3)) #S(3D_POINT X 1 Y 2 Z 3) (3d_point-z some-3d-point) 3 (setf (3d_point-z some-3d-point) 4) 4 (3d_point-z some-3d-point) 4 (defstruct 2d_point x y) 2D_POINT (setf some-2d-point (make-2d_point)) #S(2D_POINT X NIL Y NIL) (2d_point-p some-2d-point) T (2d_point-p some-3d-point) NIL
Other data types are offered by Lisp and are described in the reference books. Of these, you may also find use for:
You can create an array using the make-array command:
Individual array cells are accessed using aref:
You can inquire the rank and dimensions of an array:
(setf 1x10-array (make-array 10)) #(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL) (setf 2x2-array (make-array '(2 2) :initial-element 0)) #2A((0 0) (0 0)) (setf (aref 2x2-array 0 1) "foo") "foo" (aref 2x2-array 0 1) "foo" (array-rank 1x10-array) 1 (array-dimensions 2x2-array) (2 2) (aref 2x2-array 2 2) Error: The first index, 2, to the array #2A((0 "foo") (0 0)) is too large.
In the ME Macro language, you write macros to perform specific tasks. Lisp allows you to define functions using defun instead.
DEFINE Hello_world DISPLAY "Hello, world!" END_DEFINE (defun hello-world () (format nil "Hello, world!"))
Note: Lisp also contains "macros". Lisp macros work like functions, but are evaluated differently than functions by the Lisp interpreter. Lisp macros can be very powerful - and a bit confusing. You don't need to use them at first. The reference books say more about them if you're interested. From now on, when the word "macro" is used in this document, it will refer to ME Macros, not Lisp macros.
Lisp functions can take parameters, just like macros.
DEFINE Print_message PARAMETER Message DISPLAY Message END_DEFINE (defun print-message (message) (print message))
Notice that you don't have to specify the data type of the parameter "message" in either language (in this case, "string"). In the macro, you just declare it using the keyword PARAMETER. In Lisp, you name the parameter inside a pair of parentheses immediately following the function name.
Unlike Macro parameters, however, you can also specify Lisp parameters to be:
You define these special behaviors for the parameters when declaring them.
A parameter is optional if you declare it using &optional. The function show-parameters has one required parameter (p1) and two optional parameters (p2 and p3):
(defun show-parameters (p1 &optional p2 p3) (format nil "p1 is: ~a~%p2 is : ~a~%p3 is : ~a" p1 p2 p3)) (show-parameters 1) "p1 is: 1 p2 is : NIL p3 is : NIL" (show-parameters 1 2) "p1 is: 1 p2 is : 2 p3 is : NIL" (show-parameters 1 2 3) "p1 is: 1 p2 is : 2 p3 is : 3"
The optional parameters can be assigned default values so that they don't come up NIL if no values are passed for them:
(defun show-parameters (p1 &optional (p2 2) (p3 3)) (format nil "p1 is: ~a~%p2 is : ~a~%p3 is : ~a" p1 p2 p3)) (show-parameters 1) "p1 is: 1 p2 is : 2 p3 is : 3" (show-parameters 1 "two" "three") "p1 is: 1 p2 is : two p3 is : three"
You can also pass an arbitrary number of parameters to a function. Any parameter following &rest will bundle all remaining values passed to a function into a single list.
(defun show-parameters (p1 &rest other-parameters) (format nil "p1 is: ~a~%other-parameters are: ~a" p1 other-parameters)) (show-parameters 1) "p1 is: 1 other-parameters are: NIL" (show-parameters 1 2 3 4 5) "p1 is: 1 other-parameters are: (2 3 4 5)"
You can also identify parameters by keywords. Any parameter identified by a keyword is optional and can have a default value defined for it. The keywords are a form of documentation, allowing you to pass values to your function in any order in a readable way.
(defun show-parameters (&key (p1 1) (p2 2) (p3 3)) (format nil "parameter1 is: ~a~%parameter2 is: ~a~%parameter3 is: ~a" p1 p2 p3)) (show-parameters ) "parameter1 is: 1 parameter2 is: 2 parameter3 is: 3" (show-parameters :p1 "one" :p2 "two" :p3 "three") "parameter1 is: one parameter2 is: two parameter3 is: three" (show-parameters :p1 "one" :p3 "three") "parameter1 is: one parameter2 is: 2 parameter3 is: three" (show-parameters :p3 "three" :p1 "one") "parameter1 is: one parameter2 is: 2 parameter3 is: three"
By default, a keyword is the variable name prefixed with a colon. A keyword must start with a colon, but it can have a value other than the variable name.
You can combine all of these parameter types in many ways. The reference books define the possibilities more completely and give further examples.
Lisp functions can have local variables, just like Macros.
DEFINE Circle_area PARAMETER Diameter LOCAL Radius LET Radius (Diameter / 2.0) DISPLAY (3.141592654 * Radius * Radius) END_DEFINE (defun circle-area (diameter) (let (radius) (setf radius (/ diameter 2.0)) (* pi radius radius)))
In the Lisp function circle-area, the local variable radius is defined using the let statement. It is later assigned a value using setf. Notice that the other commands in the function are defined within the parentheses of the let statement. The parentheses define the scope of the variable. Outside these parentheses, the variable named radius is unknown.
You can define more than one variable using a let statement. You can also assign them initial values.
(defun annular-area (outer-diameter inner-diameter) (let ((outer-radius (/ outer-diameter 2.0)) (inner-radius (/ inner-diameter 2.0))) (* pi (- (expt outer-radius 2) (expt inner-radius 2)))))
One nice thing about the fact that Lisp functions always return values is that you often don't need so many local variables.
(defun annular-area (outer-diameter inner-diameter) (* pi (- (expt (/ outer-diameter 2.0) 2) (expt (/ inner-diameter 2.0) 2))))
In your Macro programs, you took different actions for different conditions using the IF command with some kind of test expression:
IF (((ABS Integer) MOD 2) = 0) DISPLAY ((STR Integer) + " is even") ELSE DISPLAY ((STR Integer) + " is odd") END_IF
Lisp has several conditional statements for this purpose as well:
Remember that in Lisp, an expression is TRUE if its return value is anything other than NIL. A return value of NIL makes an expression FALSE.
(when (= 3 3) (format nil "three + three = ~a" (+ 3 3))) "three + three = 6" (when (= 3 4) (format nil "three never equals four")) NIL (unless (= 3 4) (format nil "three never equals four")) "three never equals four" (unless (= 3 3) (format nil "three equals three, of course!")) NIL (if (= 3 3) (format nil "three equals three") (format nil "three really DOES equal three")) "three equals three" (if (/= 3 3) (format nil "three equals three") (format nil "three really DOES equal three")) "three really DOES equal three" (defun cond-example (number1 number2) (cond ((< number1 number2) (format nil "~a < ~a" number1 number2)) ((= number1 number2) (format nil "~a = ~a" number1 number2)) ((> number1 number2) (format nil "~a > ~a" number1 number2)))) (cond-example 1 2) "1 < 2" (cond-example 2 2) "2 = 2" (cond-example 2 1) "2 > 1" (defun case-example (number) (case number ((1 2 3) "one-two-three") ((4 5 6) "four-five-six") (7 "lucky seven!") (otherwise "no luck..."))) ;; otherwise matches anything (case-example 2) "one-two-three" (case-example 4) "four-five-six" (case-example 7) "lucky seven!" (case-example 10) "no luck..."
Test expressions usually involve a comparison. You've already seen functions that compare numbers and strings. Here are three more general comparison functions:
(defun equality-test (object1 object2) (cond ((eq object1 object2) (format nil "~a eq ~a" object1 object2)) ((eql object1 object2) (format nil "~a eql ~a" object1 object2)) ((equal object1 object2) (format nil "~a equal ~a" object1 object2)) (t (format nil "~a does not eq, eql, or equal ~a" object1 object2)))) ;; t is always true (setf x '(1 2 3)) (1 2 3) (setf y x) (1 2 3) (equality-test x y) "(1 2 3) eq (1 2 3)" (equality-test 2.5 (/ 5.0 2.0)) "2.5 eql 2.5" (equality-test "foo" "foo") "foo equal foo" (equality-test '(1 2 3) '(1 2 3)) "(1 2 3) equal (1 2 3)" (equality-test '(1 2 3) '(1 2 3 4)) "(1 2 3) does not eq or equal (1 2 3 4)"
Lisp offers so many comparison operators to let you use the most computationally efficient one for the task. All of this freedom of choice can be confusing. Practically speaking, you can:
You can construct logical combinations of expressions using the following boolean operators:
(and (< 1 2) (= 2 2) (string= "foo" "foo")) T (and (< 1 2) (/= 2 2) (string= "foo" "foo")) NIL (or (< 1 2) (= 2 2) (string= "foo" "foo")) T (or (< 1 2) (/= 2 2) (string= "foo" "foo")) T (or (> 1 2) (/= 2 2) (string= "foo" "bar")) NIL (not (or (> 1 2) (/= 2 2) (string= "foo" "bar"))) T
You can group multiple Lisp expressions together into a single expression having one return value using:
(progn (+ 1 1) (* 2 2) (- 3 2)) 1 (prog1 (+ 1 1) (* 2 2) (- 3 2)) 2 (if (= x y) (incf identical-count 1) ;; increment count by one (progn (incf different-count 1) (setf sums (+ (+ x y) sums))))
You will use progn frequently in Creo Elements/Direct Modeling for tasks like executing several functions when a single button is pushed.
Your Macros probably included some iteration commands like LOOP, WHILE, and REPEAT. Lisp offers several iteration commands as well:
(defun loop-example (number) (let ((counter 0)) (loop (print counter) (incf counter) (when (= counter number) (return "done"))))) (loop-example 3) 0 1 2 "done" (dotimes (counter 3 "done") (print counter)) 0 1 2 "done" (dolist (counter '(0 1 2) "done") (print counter)) 0 1 2 "done" (mapcar #'+ '(1 2 3) '(4 5 6)) (5 7 9)
One of the most natural methods of iteration in Lisp is recursion, where a function calls itself repeatedly until some end condition is satisfied.
(defun recursive-example (number) (if (< number 0) (values "done") ; returns the value "done" (progn (print number) (recursive-example (- number 1))))) (recursive-example 2) 2 1 0 "done"
Similar to the Macro language, Lisp allows you to write to or read from a file by first opening a stream associated with the file. You can open a stream using the open command. When you are finished with a stream, you must close it.
OPEN_OUTFILE 9 (setf file-stream (open "/tmp/output_file" :direction :output)) -- OR -- OPEN_INFILE 9 "/tmp/input_file" (setf file-stream (open "/tmp/input_file" :direction :input)) -- AND -- CLOSE_FILE 9 (close file-stream)
You can write to an open file using the format command shown earlier:
(setf file-stream (open "/tmp/outfile" :direction :output)) (format file-stream "Output value to file: ~a~%" my-value) (close file-stream)
You can read a line from an open file using:
You could use these commands in combination to copy one file to another:
(defun copy-file (input-file output-file) (let (input-line (input-stream (open input-file :direction :input)) (output-stream (open output-file :direction :output))) (loop (setf input-line (read-line input-stream nil 'eof)) (if (eq input-line 'eof) (progn (close output-stream) (close input-stream) (return)) (format output-stream "~a~%" input-line)))))
Lisp provides the convenient function with-open-file to take care of opening and closing the files for you.
(defun copy-file (input-file output-file) (let (input-line input-stream output-stream) (with-open-file (input-stream input-file :direction :input) (with-open-file (output-stream output-file :direction :output) (loop (setf input-line (read-line input-stream nil 'eof)) (if (eq input-line 'eof) (return) (format output-stream "~a~%" input-line)))))))
In Lisp, you can create a lot of "garbage" when performing list operations. Garbage is memory consumed by data that you don't need. Lisp is very conscientious about collecting the garbage on a regular basis and freeing the memory, but this tedious task slows down your program. You can avoid creating garbage by choosing the right list operation for the task.
Lisp offers several debugging commands:
(apropos "foo") FOO2 has value: "foo2" FOO-FUNC Function FOO has value: "foo" (trace copy-file) (COPY-FILE) (copy-file "/tmp/old-file" "/tmp/new-file") 1> (COPY-FILE "/tmp/old-file" "/tmp/new-file") <1 (COPY-FILE NIL) NIL (untrace copy-file) (COPY-FILE) (copy-file "/tmp/old-file" "/tmp/new-file") NIL (copy-file "/tmp/no-file" "/tmp/new-file") Error: Cannot open the file /tmp/no-file. Fast links are on: do (use-fast-links nil) for debugging Error signalled by OPEN. Broken at OPEN. Type :H for Help. >>:b Backtrace: system:top-level > eval > copy-file > let > OPEN NIL
The Yuasa book describes these comands in more detail and shows more examples.
The common text editors vi and emacs provide some support for writing Lisp programs.
You can start vi in -l mode or use :se lisp to edit Lisp files. It will then offer you the following commands:
Use :set sm to set the showmatch option on. This will cause vi to briefly jump backward to the matching opening parenthesis ( when a closing parenthesis ) is typed.
Please refer to the UNIX man pages on vi or your favorite vi reference book for more information.
Emacs is written in Lisp, so it offers lots of support for Lisp programming if you really get into it. Some basic commands, equivalent to what vi offers, are:
Emacs automatically goes into lisp mode when you edit files ending in ".lsp". It will then indent using a standard Lisp format.
Shown below is a sample Lisp program that will plot some 2D points on a grid using definable ASCII characters. If you'ld like to try running the program:
Read through the program to see how it works. Then you can modify it, or write your own program to get some practice writing Lisp code.
; -*-Lisp-*- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Description: Example Lisp program that plots some characters in 2D space ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Constants and globals defining plotting grid characteristics (defconstant *axis-char* "+") (defconstant *no-value-char* ".") (defconstant *default-plot-char* "*") (defvar *h-max* 40) (defvar *v-max* 20) (defvar *rows* (+ *v-max* 1)) (defvar *columns* (+ *h-max* 1)) ;; Define structures (defstruct plot_point v h (char *default-plot-char*)) ;; Plotting functions (defun make-grid () (initialize-grid (make-array (list *rows* *columns*)))) (defun initialize-grid (grid) (let (row column) (dotimes (row *rows* grid) (dotimes (column *columns*) (if (or (= row 0) (= column 0)) ; this is an axis point (setf (aref grid row column) *axis-char*) (setf (aref grid row column) *no-value-char*)))))) (defun set-grid-points (point-list grid) (dolist (point point-list grid) (set-grid-point point grid))) (defun set-grid-point (point grid) (let ((h (plot_point-h point)) (v (plot_point-v point))) (cond ((not (eq (type-of h) 'fixnum)) nil) ; h not an integer, ignore point ((not (eq (type-of v) 'fixnum)) nil) ; v not an integer, ignore point ((or (> h *h-max*) (< h 0)) nil) ; h out of range, ignore point ((or (> v *v-max*) (< v 0)) nil) ; v out of range, ignore point (t (setf (aref grid v h) (plot_point-char point)))))) (defun plot-grid (grid) (let (row column) (dotimes (row *rows* 'done) (format t "~%") (dotimes (column *columns*) (princ (aref grid row column)))))) ; print value w/o line feed ;; Demonstration functions (defun show-example () (plot-grid (set-grid-points (make-example-points) (make-grid)))) (defun make-example-points () (list (make-plot_point :v 4 :h 22 :char "_") (make-plot_point :v 4 :h 23 :char "_") (make-plot_point :v 5 :h 21 :char "/") (make-plot_point :v 5 :h 23 :char "/") (make-plot_point :v 5 :h 24 :char "\\") (make-plot_point :v 6 :h 20 :char "/") (make-plot_point :v 6 :h 22 :char "/") (make-plot_point :v 6 :h 25 :char "\\") (make-plot_point :v 7 :h 12 :char "_") (make-plot_point :v 7 :h 13 :char "_") (make-plot_point :v 7 :h 19 :char "/") (make-plot_point :v 7 :h 20 :char "_") (make-plot_point :v 7 :h 21 :char "/") (make-plot_point :v 7 :h 23 :char "/") (make-plot_point :v 7 :h 24 :char "\\") (make-plot_point :v 7 :h 26 :char "\\") (make-plot_point :v 8 :h 11 :char "/") (make-plot_point :v 8 :h 12 :char "_") (make-plot_point :v 8 :h 13 :char "/") (make-plot_point :v 8 :h 14 :char "\\") (make-plot_point :v 8 :h 17 :char "_") (make-plot_point :v 8 :h 18 :char "_") (make-plot_point :v 8 :h 19 :char "\\") (make-plot_point :v 8 :h 21 :char "\\") (make-plot_point :v 8 :h 23 :char "\\") (make-plot_point :v 8 :h 24 :char "_") (make-plot_point :v 8 :h 25 :char "\\") (make-plot_point :v 8 :h 27 :char "\\") (make-plot_point :v 9 :h 11 :char "\\") (make-plot_point :v 9 :h 13 :char "\\") (make-plot_point :v 9 :h 15 :char "\\") (make-plot_point :v 9 :h 16 :char "/") (make-plot_point :v 9 :h 18 :char "/") (make-plot_point :v 9 :h 19 :char "\\") (make-plot_point :v 9 :h 20 :char "\\") (make-plot_point :v 9 :h 22 :char "\\") (make-plot_point :v 9 :h 24 :char "\\") (make-plot_point :v 9 :h 25 :char "/") (make-plot_point :v 9 :h 27 :char "/") (make-plot_point :v 10 :h 12 :char "\\") (make-plot_point :v 10 :h 14 :char "\\") (make-plot_point :v 10 :h 16 :char "\\") (make-plot_point :v 10 :h 17 :char "/") (make-plot_point :v 10 :h 20 :char "\\") (make-plot_point :v 10 :h 21 :char "\\") (make-plot_point :v 10 :h 23 :char "\\") (make-plot_point :v 10 :h 26 :char "/") (make-plot_point :v 11 :h 13 :char "\\") (make-plot_point :v 11 :h 15 :char "\\") (make-plot_point :v 11 :h 18 :char "/") (make-plot_point :v 11 :h 19 :char "\\") (make-plot_point :v 11 :h 21 :char "\\") (make-plot_point :v 11 :h 22 :char "\\") (make-plot_point :v 11 :h 24 :char "\\") (make-plot_point :v 11 :h 26 :char "\\") (make-plot_point :v 12 :h 14 :char "\\") (make-plot_point :v 12 :h 16 :char "\\") (make-plot_point :v 12 :h 18 :char "\\") (make-plot_point :v 12 :h 20 :char "\\") (make-plot_point :v 12 :h 22 :char "\\") (make-plot_point :v 12 :h 23 :char "\\") (make-plot_point :v 12 :h 25 :char "\\") (make-plot_point :v 12 :h 27 :char "\\") (make-plot_point :v 13 :h 15 :char "\\") (make-plot_point :v 13 :h 17 :char "\\") (make-plot_point :v 13 :h 19 :char "\\") (make-plot_point :v 13 :h 20 :char "_") (make-plot_point :v 13 :h 21 :char "\\") (make-plot_point :v 13 :h 22 :char "/") (make-plot_point :v 13 :h 24 :char "\\") (make-plot_point :v 13 :h 26 :char "\\") (make-plot_point :v 13 :h 28 :char "\\") (make-plot_point :v 14 :h 16 :char "\\") (make-plot_point :v 14 :h 18 :char "\\") (make-plot_point :v 14 :h 20 :char "\\") (make-plot_point :v 14 :h 25 :char "\\") (make-plot_point :v 14 :h 26 :char "_") (make-plot_point :v 14 :h 27 :char "\\") (make-plot_point :v 14 :h 28 :char "/") (make-plot_point :v 15 :h 17 :char "\\") (make-plot_point :v 15 :h 18 :char "_") (make-plot_point :v 15 :h 19 :char "\\") (make-plot_point :v 15 :h 20 :char "/"))) #| Some ideas for improvement - Vary the grid size. - Define a file format for storing the plot points. Then write a function to recreate a stored plot from a file. - Change the program so that it doesn't display the grid, or only displays it if an option is set. - Write functions to transform the points horizontally, vertically, or by rotation. - Add a scaling routine to handle an infinite range of points. |#
© 2023 Parametric
Technology GmbH (a subsidiary of PTC Inc.), All Rights Reserved |