Part Two - Spiral
I will break down the program into sections and, in turn, analyze each.
Depending on feedback received in the forum, I will add more details to these
columns.
Analyze Code
Portions of the column showing code will be in
green.
Define Main Function
(defun C:SPIRAL (/ #DIAMETER #PITCH)
...
The new SPIRAL command is defined with the defun function. The C: in front of the
function name makes it available from the command line without entering the
lisp interpreter.
The second part of the line is a list of variable names that need to be
kept local to the program. My personal convention is to use a "#" sign as
the first character of every variable name that I know should be kept local.
Another convention is to indent one character for each open parenthesis.
That makes it easier to track the nested levels that will have to be
closed.
When first testing and debugging a lisp program, I often leave many of
the variables out of this list so that they are easy to inspect at the
command line. Notice that the #ANGLE-DELTA variable is absent. After
running the program, you can check what it's value is:
Command: !#ANGLE-DELTA
0.392699
Try it with a variable that was in the variable list:
Command: !#PITCH
nil
Since #PITCH is in the local variable list, it's value is only accessible
while the program is running. So we see nil instead.
Get User Input
...
(setq #DIAMETER (getreal "\nDiameter: "))
(setq #PITCH (getreal "\nPitch: "))
(setq #COILS (getint "\nNumber of coils: "))
(setq #POINT-0 (getpoint "\nCenterpoint: "))
...
Since the program requires input from the user, we use several getXXX type
functions. getreal for real numbers, getint for integers (whole numbers),
and getpoint for an XYZ point value.
>(setq #DIAMETER (getreal "\nDiameter: "))
This line sets the #DIAMETER variable to the value that the user enters
in response to the getreal prompt. It is a "real" number so it can be any
decimal value. #DIAMETER represents the diameter of the coils that will be
drawn. The "\n" at the start of the prompt string causes a new line
to be started.
>(setq #PITCH (getreal "\nPitch: "))
Sets the #PITCH variable to the users supplied value. It too, is a real
number. #PITCH represents the vertical distance between coils.
>(setq #COILS (getint "\nNumber of coils: "))
Sets the #PITCH variable to a user supplied integer. The number of coils
could actually be a decimal number (real) but I chose to use a whole
number (integer) in order to simplify other portions of the program. A real
number would have allowed the last coil to be a partial coil which would
have to be dealt with as a special case.
>(setq #POINT-0 (getpoint "\nCenterpoint: "))
Sets the #POINT-0 variable to the user supplied point. The getpoint
function allows the user the either enter point coordinates at the keyboard
or pick a point in the AutoCAD drawing. #POINT-0 will be the starting
point for the spiral.
If the user does not enter a value for any one of the prompts,
the program will fail when it tries to draw the spiral. We can fix
this potential problem later.
Set Other Variables
...
(setq #RESOLUTION 16)
(setq #RADIUS (* #DIAMETER 0.5))
(setq #ANGLE-DELTA (/ (* 2 pi) #RESOLUTION))
(setq #ANGLE 0.0)
(setq #SEGMENTS (* #COILS #RESOLUTION))
(setq #Z-DELTA (/ #PITCH #RESOLUTION))
(setq #Z 0.0)
(setq #LIST nil)
...
The program does some preliminary calculations and sets some other
variables before it can generate the list of points to draw the spiral.
>(setq #RESOLUTION 16)
The #RESOLUTION variable is used to determine how many points make up each
coil. You could set this to another value or prompt the user for a value,
but a value of 16 should be fine for most cases. A higher resolution increases
the accuracy of the circular coils.
>(setq #RADIUS (* #DIAMETER 0.5))
Since the radius value will be more convenient to use in the calculations,
the #DIAMETER value is multiplied by 0.5 to yield the value stored in
the #RADIUS variable.
>(setq #ANGLE-DELTA (/ (* 2 pi) #RESOLUTION))
The #ANGLE-DELTA variable it used to store the incremental angle between
each point around the perimeter of the coils. Angles in lisp are stored in
radians, so pi is used to calculate the angle between each point.
>(setq #ANGLE 0.0)
#ANGLE is the starting angle for the first coil in the spiral.
>(setq #SEGMENTS (* #COILS #RESOLUTION))
#SEGMENTS is the total number of segments or that make up the spiral.
>(setq #Z-DELTA (/ #PITCH #RESOLUTION))
#Z-DELTA is the incremental distance between each point in the Z axis as
we move around the coils. It is the distance between coils or #PITCH,
divided by the number of point in each coil or #RESOLUTION.
>(setq #Z 0.0)
#Z is the starting elevation above #POINT-0 for the first coil in the
spiral.
>(setq #LIST nil)
Since the program does not yet have the complete list of local variables
called out, I had to set #LIST to nil. That is because I will be adding to
#LIST with the cons function and I need to start with a blank list. Later,
I will add #LIST to the local variables so that it will be nil when the
program starts. At that time, this line will not be needed.
Generate List of Points
...
(repeat #SEGMENTS
(setq #POINT-1 (polar #POINT-0 #ANGLE #RADIUS))
(setq #POINT-1 (list (car #POINT-1)
(cadr #POINT-1)
(+ (caddr #POINT-1) #Z)))
(setq #LIST (cons #POINT-1 #LIST))
(setq #ANGLE (+ #ANGLE #ANGLE-DELTA))
(setq #Z (+ #Z #Z-DELTA))
)
...
The program steps around the coils and increases the elevation as it
builds a list of points.
>(repeat #SEGMENTS
This line starts the repeat function. Everyting that comes before the closing
")" will be repeated the number of times specified by #SEGMENTS.
>>(setq #POINT-1 (polar #POINT-0 #ANGLE #RADIUS))
For each pass through the repeat function, the polar coordinates of the
next point on the coil is calculated.
>>(setq #POINT-1 (list (car #POINT-1)
(cadr #POINT-1)
(+ (caddr #POINT-1) #Z)))
The #Z value is added to the Z coordinate of the XYZ point.
>>(setq #LIST (cons #POINT-1 #LIST))
The point value is added to a list of points. When the first point is added,
the cons function creates the list since one does not yet exist. Subsequent
points are added to the same list.
>>(setq #ANGLE (+ #ANGLE #ANGLE-DELTA))
The #ANGLE variable is incremented by #ANGLE-DELTA in preparation for
calculation of the next point.
>>(setq #Z (+ #Z #Z-DELTA))
The #Z elevation variable is incremented by #Z-DELTA in preparation for
calculation of the next point.
Draw Spiral Spline
...
(setq #LIST (reverse #LIST))
(command "_.spline")
(foreach #POINT #LIST (command #POINT))
(command "" "" "")
)
The program uses the list of points to draw the spiral spline.
>(setq #LIST (reverse #LIST))
Since the cons function builds the list by adding to the front, I reversed
the list to make it the correct order. I did this just to have the spiral
draw from bottom to top. The end result would look the same either way.
>(command "_.spline")
This line starts the spline command.
>(foreach #POINT #LIST (command #POINT))
Each point in the list is fed to the command interpreter which is still
expecting point inputs for the spline command that was started in the
previous line of code.
>(command "" "" "")
The first "" instructs the spline command to end it's request for more
points. The next two "" are in response to the spline commands request
for start and end tangency points. The program merely accepts the
defaults. The tangency points should really be calculated to a much higher
precision than what the defaults give.
You can run the AutoCAD spline command to see the
sequence that it uses.
Part Three will detail the modifications
needed to have accurate tangency points. I'll also start cleaning the
code up a little bit.
|