home *** CD-ROM | disk | FTP | other *** search
- Line by Line through the Random Envelope Macro
-
- by Grant Boucher
-
-
-
- One of the most powerful features of the Amiga operating system is the
- ARexx programming language. Essentially, ARexx lets you write your own
- programs that control other programs according to your needs. For example,
- you can use ARexx to write programs that will convert large numbers of
- image files from one file format to another.
-
- With the new Modeler Arexx and Macro functions, we can program our own
- custom LightWave and Modeler effects without having to know how to program
- in C or how the Modeler actual does the things it does. Just take a look
- at the vast numbers of powerful ARexx Macros, both public domain and
- commercially available, and the great deal of functionality they have added
- to the entire LightWave/Modeler combo.
-
- Recently, I was involved in some special effects shots that required
- at least a hundred lights which needed to randomly undulate in intensity.
- We also wanted the option of having them fade out towards the end. Since
- the shots were as long as 900 frames and we wanted to avoid repeating any
- motions which might make the lights look mechanical or programmed, I was
- asked to whip up a quick and dirty ARexx script to do the job. Once the
- LightWave envelopes were generated, I was then asked to give the macro a
- gui interface and add some features that might make it a little more useful
- for future effects shots.
-
- That's where Modeler came in and and I felt the finished Modeler Macro
- seemed a perfect tutorial for those not yet initiated into Arexx
- programming the Modeler.
-
-
-
-
-
- ARexx - Basic principles
-
- ARexx is actually just a typical programming language with an
- attitude. You'll find all of the standard programming tools of a Basic, C,
- or Pascal style language that you'd expect, but also the extra added bonus
- of commands that come from virtually all other Amiga programs. Image
- processing programs like ImageFX and ADPro come with entire reference
- manuals listing special commands that allow you to load, process, and save
- image data...all from YOUR ARexx script!
-
- So, I like to think of ARexx as just a new version of all the
- programming languages I have used in the past, with Amiga software and
- hardware control functions thrown in.
-
- Until recently, I used ARexx primarily for batch processing of files,
- so I didn't have to manually Load Object, Flip Polygons (wait), Reduce
- Faces (wait), and Save Object for every Public Domain Imagine object I
- wanted to convert to LightWave using Pixel 3D Professional, for example.
-
-
-
- Writing ARexx Scripts
-
- Since ARexx programs are just text files that the RX program
- interprets, you can use any word processor like Final Writer or a text
- editor like CygnusEd to write and edit your ARexx script. If you use a
- Word Processor, make sure you import and export your file as ASCII text, as
- the word processor's own internal file format will certainly add strange
- control characters that mean nothing to ARexx and make your script
- unusable.
-
-
-
-
-
- The Program - line by line
-
- Line 1 - The Author Line
-
- The first line of any ARexx script must be a comment line. It is just
- ARexx's way of signifying that this is an ARexx program, and not a letter
- you are sending to your mother. Comments begin with the /* characters and
- end with the */ characters. Comments are usually used to document your
- script for others or yourself if you come back to it three years later.
- Since the first line of any ARexx script must be a comment, most
- programmer's find this a convenient place to put their name for posterity.
- Note that comment lines don't affect the operation or function of your
- program, and are simply ignored when it is run.
-
-
-
- Lines 3 - 12
-
- For most Modeler macros, you'll want these lines at the start of your
- script. Basically, they initialize special ARexx math functions, turn on
- some special debugging tools, and generally check to make sure everything
- is set up right to start. Many initialization functions like this can be
- just grabbed from the start of another ARexx script and left alone. I
- don't worry about them, so probably neither should you.
-
-
-
- Line 15
-
- This line calls (or performs) Modeler's requester and interface
- definition, named REQ_BEGIN(). The text that follows this command in
- quotations becomes the name of the requester. The Modeler automatically
- formats this and all requester commands for you!
-
-
-
- Line 17
-
- This is the first of the requesters, where we actually want some
- user-input to come back. Again, we use the REQ_ADDCONTROL() function to
- set up our new feature, but this time we pass only two arguments to the
- function. The first is a text message describing to the user what this
- function means. The second is the letter 'B' which tells the function that
- this is a BOOLEAN, or ON/OFF, style requester. The modeler automatically
- creates the Check-Mark gadget for you! I gave this feature the name RampID
- so I would know that this is my Ramp to Minimum toggle. This feature, if
- on, tells the macro to decrease the overall intensity of all of the
- envelope key frames until they equal the user's lowest intensity value by
- the end of the envelope. In other words, the intensities fade towards the
- end of the envelope.
-
-
-
- Lines 18-25
-
- All of these lines are essentially the same. This time, the
- REQ_ADDCONTROL() function is passed a letter 'N' (as the second variable
- again) to signify that this is going to be a numeric requester, asking for
- a value from the user. The first argument is again a description of what
- number the user needs to enter, and the third argument tells the requester
- what units the number will be in. Since we are working with arbitrary
- values like frames and intensities, not meters, we send a 0 for all of
- these, which means 'unitless'.
-
- Note the strange %1 and %2 characters attached to these functions.
- The % character stands for INTEGER DIVIDE, meaning it is the same as a
- standard DIVIDE command (i.e. '/') but that the result is always an
- integer. When I take a number and INTEGER DIVIDE by 1 (i.e. %1) I am just
- forcing whatever value the user entered to become an integer. We have no
- use for Frame 10.333 for example!
-
- The MINFRAME and MAXFRAME variables are INTEGER DIVIDE by 2 (i.e. %2)
- because if our envelope peaks are going to be, say 30 to 50 frames apart,
- each key frame is actually going be half that amount from every other key
- frame. Two key frames, or twice that value, gets you back to where you
- started from, either a peak or valley. I kill two birds with one stone as
- I make sure these values are integers and they are reduced by half so our
- program works properly.
-
-
-
-
-
- Definitions of our main variables...
-
-
-
- MINFRAME = The minimum distance between peaks of our undulating envelope.
- For example, a value of 20 here means that no two high points (or two low
- point for that matter) will be closer than 30 frames apart.
-
-
-
- MAXFRAME = The maximum distance between peaks of our undulating envelope.
- For example, a value of 50 here means that no two high points (or two low
- point for that matter) will be farther than 50 frames apart.
-
-
-
- NOTE: If you want the envelope key frames to step forward evenly, say
- every 40 frames between peaks, then MINFRAME and MAXFRAME should be equal
- (i.e. 40 in our example above). Having these two values random means the
- intensities vary from high to low over a random time frame, giving them a
- more organic and natural feel.
-
-
-
- ENDFRAME = The length in frames of our envelope. Because we are generating
- our steps between key frames, this value is a minimum guideline only. For
- example, a value of 1000 means the last key frame of the envelope will
- certainly not be less than 1000, but it might be slightly higher.
-
-
-
- LOWMIN = This value determines the lowest intensity of our envelope.
-
-
-
- LOWMAX = This value determines the maximum range of the lowest intensities.
-
-
-
- NOTE: If LOWMIN is 0 and LOWMAX is 10, the valleys (or low key frames)
- will never be lower than 0 and never higher than 10. If LOWMIN and LOWMAX
- are equal (for example, both are 0), then there will be no variation of the
- low values (i.e. the low key frames will always be 0). Again, having
- these values different makes our lights dim in a more unpredictable
- fashion.
-
-
-
- UPPERMIN = This value determines the minimum range of our highest
- intensities.
-
-
-
- UPPERMAX = This value determines the highest intensity of our envelope.
-
-
-
- NOTE: Similar to LOWMIN and LOWMAX, UPPERMIN and UPPERMAX give us a range
- for the peaks (or maximum values) of our intensity envelope. The peak
- values will never be higher than UPPERMAX (i.e. 40) and never lower than
- UPPERMIN (i.e. 20).
-
-
-
- ENVELOPES = The total number of Envelopes you want to generate using these
- settings.
-
-
-
-
-
- Lines 27-35
-
- Once we have set up the requesters, we usually need to send them some
- default values. Note that we CALL the REQ_SETVAL() function for each
- requester to do this. The first argument is the ID name of the requester
- (for example, RampID). The second is the actual value (for example, a 1 or
- 0 for the Boolean ON or OFF). The rest of the functions and there default
- values should be self-evident.
-
-
-
- Line 37
-
- The function REQ_POST() sets up the requester with the Okay, Cancel,
- and Reset options already enabled. It waits for the user to input
- everything and select either Okay or Cancel. Reset automatically clears
- all variables. If okay is pressed, then X is set to 1 by this function.
- If Cancel is pressed, then X is set to 0.
-
-
-
- Line 39
-
- A standard IF - THEN conditional statement. If X=1 (therefore we know
- Okay was pressed) then DO everything that follows. If this check was
- failed (therefore, Cancel must have been pressed and X must be 0) then the
- program drops to the END statement that matches this IF statement (i.e.
- line 130). The next command after that is EXIT and so the program ends.
-
-
-
- With just those few lines, we set up an entire program front-end,
- prompted the user for lots of input, with default values given as examples,
- and waited for the user to either abort the program, reset the values, or
- tell us to proceed! The Modeler and ARexx do all the work for us and our
- interface is guaranteed to behave and look professional like all of the
- other Macros written for the Modeler.
-
-
-
- Lines 40-48
-
- Now that the user has told us to begin, we need to read back in the
- values he or she entered using the REQ_GETVAL() function. The only
- argument we pass to the function is the name of our ID (for example,
- EndFrameID). We then assign the value returned by the function to a
- variable (for example, ENDFRAME). We do the exact same thing for all of
- our requesters.
-
-
-
- Lines 51-55
-
- We cannot always be sure our users give us exactly what we want. To
- avoid generating an error later on in the program, we check to make sure
- that the LOWMIN value is indeed less than the LOWMAX value. From the
- user's stand point it doesn't matter, as long as the range is between two
- values, but internally our own functions require these two values to be
- ordered correctly.
-
- First we check to see IF LOWMIN is greater than LOWMAX. If it is,
- then we DO the next series of commands. If it isn't then we skip to the
- END of the IF statement (i.e. line 55) and proceed on with the program.
-
- Assuming our user entered in a LOWMIN value that was greater than the
- LOWMAX value, we need to switch these around before proceeding. First we
- assign the LOWMIN value to a temporary variable called TEMP. Now that is
- is saved for later, we set LOWMIN equal to LOWMAX, and then assign LOWMAX
- the old LOWMIN value stored in TEMP. The old variable switcheroo is a
- classic programming example. Now we can proceed on with our program
-
-
-
- Lines 57-61
-
- These lines perform the exact same function as Lines 51-55 above,
- except they assure than HIGHMIN is always less than HIGHMAX.
-
-
-
- Line 63-67
-
- These lines again perform the same function as the previous two
- examples, but instead insure than our MINFRAME offset value is less than
- our MAXFRAME offset value.
-
-
-
- Lines 69-70
-
- Now that we are sure all our data is useful, we need to prompt the
- user for some additional input. We use the GETFILENAME() function to get a
- path and root file name for the envelope(s) we are about to create. The
- first argument passed to the function is a title for the requester, while
- the second is a default path, which I chose to be LightWave's Envelopes
- drawer. As before in our program, the Modeler does all the work with the
- requester in one line! The entire path and filename is returned to the
- program and we assign that to a variable called ROOT_NAME. We then check
- to see IF ROOT_NAME equals "(none)", which is the function's message for
- "no response." If it is then we EXIT the program, otherwise we proceed with
- our program.
-
-
- Lines 72-73
-
- Using the GETFILENAME() function, we can ask our user to input a path
- and a root name for our envelope(s). The first argument passed to the
- funtion lets us name our requester for the benefit of our user. The second
- argument lets us automatically log in to a default path (you will need to
- change this to suit your needs).
- The second line checks to see if the user failed to enter anything or
- selected abort, in which case the requester returns the string "(none)" and
- therefore we exit the program.
-
-
- Line 76
-
- Here begins the main body of our program. This first line begins a
- loop (called a DO loop) where we will step the variable N from 1 to the
- variable ENVELOPES which our user entered earlier (Default is 1). All of
- the program between this line and the matching END statement for this DO
- loop (i.e. line 129) will be run again and again until N = ENVELOPES, at
- which point the program drops to line 130, which ENDS the main IF
- conditional, and then EXITs the program.
-
-
- Lines 77-80
-
- Users who have previously programmed will recognize the following
- lines as one of those annoying little file-management necessities. Since
- we might be creating hundreds of envelopes, we can give them all unique
- names by appending a number to the end of the root name. However, when you
- append the number 2 directly to a filename (i.e. Random.2) and a 10 to
- that same root name (i.e. Random.10) programs and requesters that sort
- those values return them in the wrong order (i.e. Random.10, Random.2).
- What we really need is to 'pad' out the number with leading zeros (i.e.
- Random.002, Random.010, Random.100).
-
- The first line sets our PAD variable to a null string "", with nothing
- in it. Then we check to see if N, our envelope number, is less than 100.
- If it is, add a "0" to our PAD string. Next, if N is less than 10, we need
- to add another "0" to our PAD string. Finally, our FILE_NAME is set to
- equal our ROOT_NAME combined with our PAD string and finally combined with
- N, our envelope number. NOTE that strings are combined, or concatenated in
- programmer's parlance, with the || characters (i.e. strike shift '\'
- twice).
-
-
-
- Lines 82-85
-
- We are now ready to open our new envelope file! We call the ARexx
- OPEN() function to do this. The first argument (i.e. 'out') is the name
- our program uses to address this file. It can be anything we want, but
- make sure that each file you have open at the same time has a unique name,
- to keep from getting the open files confused). The second argument is the
- actual AmigaDos path and file name of the file we are opening (i.e. the
- variable FILE_NAME we just created above). The third argument 'W' tells
- the OPEN() function that we want to Write to this file. To read from a
- file use the 'F' argument.
-
- Now that are file is created and open, we send it some initializing
- lines that LightWave requires to recognize this text file as a legitimate
- LightWave envelope file). The WRITELN() function sends a complete line of
- text to our file. We call the function with the ARexx file name of our
- file (i.e. 'out') as the first argument, and the actual text we want to
- write out (i.e. 'LWEN') and the second argument. The next two lines write
- additional data required by LightWave. Do not change any of these three
- lines or LightWave will not recognize your file!
-
-
-
- Line 87
-
- This line initializes our LASTFRAME variable to 0 each time we begin
- writing a new envelope. The LASTFRAME variable is used to keep track of
- the last frame number we have processed during our program.
-
-
-
- Line 88
-
- This line initializes our K variable to 1 each time we begin writing a
- new envelope. K is used to keep track of the actual number of key frames
- we have created in a given envelope.
-
-
-
- Line 89
-
- We need to decide whether our envelope begins within a valley or a
- peak (i.e. using LowMin/LowMax or UpperMin/UpperMax as values). We could
- have just picked a value, either 0 for valley or 1 for peak for all of our
- envelopes, but again I chose to make the results as random as possible.
-
- My variable MINMAX is going to alternate between 0 and 1 throughout
- the program, so I want it to start with a random value between 0 and 1. I
- assign MINMAX the result of the ARexx RANDOM() function, using the
- arguments 0 and 1 as the minimum and maximum values for the function. The
- result of the RANDOM() function is always an integer, so it can only return
- a 0 or a 1.
-
-
-
- Line 91
-
- Before we begin our loop to generate random values and key frames
- (i.e. line 92), we are going to get a little fancy and take advantage of
- one of Modeler's built-in interface features, the progress meter!
-
- We call the METER_BEGIN() function with two arguments. The first one
- is the number of steps we expect our meter to be updated by. For example,
- if we knew we were going to be processing 10 key frames, we might send a
- value of 10 here, so that we'd see the meter update 10 times during our
- upcoming loop. The second argument is the name or title that we pick for
- the meter requester.
-
- Our only problem here is that we don't really know exactly how many
- key frames we are going to create because the offsets between them are
- generated randomly (see below). Since the meter will error if we step it
- too many times, but not if we step it too few, it is better to err on the
- side of caution. We know that the minimum distance between key frames is
- the variable MINFRAME and the maximum frame count of our envelope is the
- variable ENDFRAMES, so the maximum number of key frames we could have in
- our envelope is ENDFRAMES%MINFRAME (remember % is just an INTEGER divide).
- We pass that function as the first argument to our meter function.
-
-
-
- Line 92
-
- Now we are ready to begin generating the key frames and their
- intensities one after another, until we reach or exceed the EndFrame value
- of our program. Our control for this DO loop is the UNTIL keyword and
- LASTFRAME > ENDFRAMES condition. In other words, the program lines
- contained with the DO loop and its accompanying END statement (i.e. line
- 102) will be repeated forever until the value of the LASTFRAME variable
- exceeds the value of the ENDFRAMES variable.
-
-
-
- Line 94
-
- If our MINMAX variable = 0 then we have a valley and our INTENSITY
- should be based on the RANDOM() value between LOWMIN and LOWMAX.
- Otherwise, proceed to the next line of our program.
-
-
-
- Line 95
-
- If our MINMAX variable = 1 then we have a peak and our INTENSITY
- should be based on the RANDOM() value between UPPERMIN and UPPERMAX.
- Otherwise, proceed to the next line of our program. By this time, we have
- covered both of the possible values of MINMAX, 0 and 1.
-
-
-
- Line 96
-
- The actual frame number for the above INTENSITY variable is determined
- by adding a RANDOM() frame offset between MINFRAME and MAXFRAME to the
- LASTKEY value of the previously created key frame. In this fashion, our
- key frames keep increasing one after another by a random amount.
-
-
-
-
-
- NOTE that we are creating an ARRAY of the variables INTENSITY and FRAME by
- creating variables with the ".k" suffix. For example, if K is 56, then we
- are working with INTENSITY.56 and FRAME.56. Each of these variables is
- unique and can have their own distinct values. ARexx automatically sets up
- variable space for our array as we go! This is usually a BIG pain in most
- programming languages.
-
-
-
- Line 97
-
- There is only one instance when we know what the frame value of a
- given key frame before hand. The first key frame (i.e. K=1) is always 0.
- In this line we check to see if K=1, and if it is we make the FRAME value
- equal to 0 automatically.
-
-
-
- Line 98
-
- We update the LASTKEY to our current FRAME value, so that next time
- around we build a new key frame starting on the value of this one.
-
-
-
- Line 99
-
- We add 1 to K, which keeps internal track of our total number of
- created key frames so far.
-
-
-
- Line 100
-
- Through a clever and standard programmer's trick, we force MINMAX to
- constantly toggle between 0 and 1 every time we pass through this program
- loop by subtracting MINMAX from 1 and then assigning the result to MINMAX
- again. If MINMAX was 0 before then 1 - 0 = 1, so MINMAX becomes 1. If
- MINMAX was 1 before then 1 - 1 = 0, so MINMAX now becomes 0 the next time
- around.
-
-
-
- Line 101
-
- To update our meter to show we have proceeded one time through our
- loop, we call the METER_STEP() function. There are no arguments as Modeler
- again takes care of all its own housekeeping.
-
-
-
- Line 102
-
- The END of this DO loop. The program now checks to see if the updated
- LASTFRAME value is greater than the ENDFRAMES value (see Line 92). If it
- isn't we start this loop again, creating more random intensities,
- offsetting new frame numbers, toggling MINMAX, increasing our key frame
- total, K. If it finally is, we drop to the next line of our program.
-
-
-
- Line 103
-
- Now that we have completed all of our calculations, it is time to
- close that requester we created in Line 91 that we've been stepping through
- during that last loop. We CALL the METER_END() function with no arguments
- to do this.
-
-
-
- Line 106
-
- We automatically added 1 to K at the end of that last loop, but since
- LASTFRAME has to be greater than ENDFRAMES by this point in the program,
- there never were any values assigned for INTENSITY and FRAME for this last
- value of K. Since this is our total number of key frames pointer, we
- should make sure this is accurate by subtracting 1 from the final value of
- K. Everything matches up now.
-
-
-
- Line 107
-
- Remember that envelope file we opened up and initialized way back at
- lines 82-85? Well, LightWave requires one more thing before it starts
- reading all your intensity and key frame values...the total number of key
- frames in the envelope - our variable K. Good thing we kept track of that,
- huh?
-
- We call the WRITELN() function and send the value of K as the second
- argument to the program file identified as 'out' by us. You should be
- getting use to the WRITELN() function by now.
-
-
-
- Line 109
-
- Because of a feature I added in later, I have to run our intensities
- through some more calculations if the user has requested us to ramp the
- intensities down to our LOWMIN value across the envelope. Since we did not
- know the final key frame until after everything was calculated, we could
- not perform this function until now. We begin with a simple IF statement
- that checks to see if RAMP=1, meaning 'yes, ramp the values'. If it is, we
- do everything between lines 110 and 115. If not, we drop through to the
- END statement on line 116 and proceed with the program. Note that ARexx
- assumes the line IF RAMP THEN DO means if RAMP is BOOLEAN TRUE (numerical
- shortcut is 1). You do not need to expressly say IF RAMP=1 THEN DO.
- Notice that we could change line 37 to read IF X THEN as well, but I wanted
- to give you an example of both for this programming tutorial. Notice how
- well Boolean logic works with Boolean requester function like the RampID.
-
-
-
- Line 110
-
- Let's start a new meter for our ramp operation by calling the
- METER_BEGIN() function like we did in line 91 earlier. However, since we
- now know the total number of key frames we have to process (i.e. K), we
- can tell the meter that we are sure to step K times through the meter as
- our first argument.
-
-
-
- Line 111
-
- We set up a DO loop that will iterate the variable 'I' starting from 1
- and ending with K. The END of this loop is on line 114.
-
-
-
- Line 112
-
- Now we are going to Ramp our intensities lower until the final key
- frame is always equal to LOWMIN.
-
- Our formula does the following:
-
-
-
- A) First it subtracts the LOWMIN value from the previous INTENSITY value so
- we are only going to scale in the range greater than LOWMIN.
-
-
-
- B) Then we calculate the percentage of ramping by dividing the current
- FRAME value by the maximum frame value represented by LASTFRAME value.
- This value will range between 0 (at the beginning) and 1 (at the end)
- during the envelope.
-
-
-
- C) Since we want to ramp DOWN, we subtract the result from B) from 1 so
- that the percentage is reversed.
-
-
-
- D) We then multiply the percentage from C) times the adjusted FRAME value.
-
-
-
- E) Finally, we add LOWMIN again so that everything is shifted back the way
- we started and reassign the new value to FRAME.
-
-
-
- Can you figure out how to ramp up to UPPERMAX across the envelope?
- This is our only nasty formula requiring some genuine math logic. Sorry
- about that.
-
-
-
- Line 113
-
- We tell the progress meter to step forward since we've completed one
- key frame by calling the METER_STEP() function as before.
-
-
-
- Line 114
-
- END the loop and step forward until it reaches the final value of K.
-
-
-
- Line 115
-
- We are done with the ramp progress meter by this point in the program
- so close the meter with a CALL METER_END() function.
-
-
-
- Line 116
-
- END of the conditional IF statement that started this whole ramping
- mess. On with the rest of the program.
-
-
-
- Line 119
-
- Let's open one final progress meter to begin wrapping up this
- envelope. Again we'll step K times through the meter.
-
-
-
- Line 120
-
- We're going to loop from 1 to K again, with a DO loop like above.
- This loop ENDs on Line 125.
-
-
-
- Line 121
-
- By calling the WRITELN() function again, we write our INTENSITY values
- to the 'out' file. We divide the INTENSITY by 100 because that's how
- LightWave likes it.
-
-
-
- Line 122
-
- The WRITELN() function sends the FRAME and preset spline control
- settings (i.e. "0.0 0.0 0.0" is Tension Continuity Bias) to our 'out'
- file.
-
- You should now be able to figure a way for the user to set default or
- random spline settings in your version of the program.
-
-
-
- Line 123
-
- Let's step that meter now that we've written out another key frame.
-
-
-
- Line 125
-
- END of the DO loop in line 120 that sends us back to the next I value
- until it reaches K.
-
-
-
- Line 126
-
- We have finished stepping our meter, so let's close it out.
-
-
-
- Line 127
-
- We CALL the CLOSE() function to close the 'out' file we've been
- creating all this time. It is now complete and can be loaded in
- LightWave's intensity envelope editor.
-
-
-
- Line 129
-
- This is the END of the DO loop in line 76. We'll start creating a new
- envelope until N reaches the value of ENVELOPES at which point we will go
- to the next line of the program
-
-
-
- Line 130
-
- This is the END of our conditional IF that started this whole process.
- The use has either pressed Cancel or all of the envelopes have been
- generated. In either case, proceed to the next line of the program.
-
-
-
- Line 131
-
- EXIT the program and return Modeler control to the user.
-
-
-
- END OF PROGRAM
-
-
-
-
-
- WHEW!!! Hopefully, this showed you enough of the basic Modeler macro
- programming functions and philosophy to get you started. In your
- Toaster/Arexx_Examples/LWM directory there is a text file called
- ModelerARexx.doc. Everything else you need to know to program the Modeler
- can be found there and most of it shouldn't look like APL to you after this
- example. For examples, just hunt and peck through the abundance of Arexx
- macros until you find something you need.
-
- Before wrapping this example up, I'd like to thank Stuart Ferguson for
- adding these features to the new Modeler. Maybe next program I'll actually
- use more of the Modeler's features than just its interface handling! I
- have some old celestial mechanics code I did in high school that's just
- dying for a rewrite.
-
- I would also like to thank Arnie Cachelin of NewTek for his abundance
- of Modeler ARexx macros I stole from heavily to produce this program. I
- can only hope you readers do the same from me.
-
-