%! % $Id: flutools.ps,v 1.2 2003/03/11 03:41:24 tomj Exp $ % % This version hacked for the WPS Model 423 XYZ Plotter. The % DACs are 10 bits, or 1023 full scale. The usable paper dimensions % are 7" x 10". Because this script assumes the same resolution % for X and Y, we restate resolution and scale as: % % resolution: 100 dpi % X full scale: 700 % Y full scale: 1000 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % PostScript $99 XY TABLE FLUTTERWUMPER TOOLKIT % ======================================================== % by Don Lancaster Revised January 13, 1997 % =================================================================== % Copyright c 1997 by Don Lancaster and Synergetics, Box 809, % Thatcher AZ, 85552. (520) 428-4073 www.tinaja.com don@tinaja.com % All commercial rights and all electronic media rights are % *fully* reserved. Linking welcome. Reposting expressly forbidden. % =================================================================== % % SUMMARY: A Flutterwumper is any hackable entity that moves and % either chomps or spits. This is a very powerful set of % preliminary PostScript tools, utilities, readers, test % files, emulators, and demos as required for preliminary % homebrew PIC flutterwumper development. % % Tutorial may be read as textfile. % Acrobat Distiller or GhostScript required for use % % See FLUTWUMP.PDF and POSTFLUT.PDF. % % Copyright c 1994, 1997 by Don Lancaster. All rights reserved. % PIC sourcecode and full consulting services available. % Free help line and additional info: (520) 428-4073. % % ============================================================================ % % Name of textfile: FLUTOOLS.PS % Source: SYNERGETICS % Author: Don Lancaster % Desc: PS $99 XY table flutterwumper toolkit % Date: September 24, 1994 July 14, 1997 % Release: 3.0 % Approx length: 43K % Status: Copyright 1994,1997 by Don Lancaster and Synergetics. % 3860 West First Street, Thatcher, AZ. (520) 428-4073. % All commercial rights and all electronic media rights % fully reserved. Personal use is permitted so long as % this status message stays present and intact. % Basic flutterwumper Infopack $75 VISA/MC. Free help % line and additional info (520) 428-4073. % % Keywords: PostScript, guru, xy, x-y, table, flutterwumper, % vector, raster, driver, utility, tool, tutorial, hack % hardware, software, interface, hack, stepper, Santa, % Claus, PIC, %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % This particular utility set has been optimized to work with Acrobat % Distiller or GhostScript on Windows 95. Example data files are % written and read from the A: drive. PS printer-based utilities % using print statements instead of disk writes are equally possible. % PS $99 XY TABLE FLUTTERWUMPER TOOLKIT % ===================================== % by Don Lancster % This toolkit consists of PS-to-step methods for getting from PostScript % to the individual XY steps needed in a $99 flutterwumper, or any other % automated positioning system. % A (usually) serial and extremely low level communication format called % a FLUTFILE is created. A flutfile simply itemizes each and every individual % table step or action as one ASCII character. Thus DRAMATICALLY minimizing % the smarts needed by the flutterwumper itself. % The input to these tools can be largely UNMODIFIED PostScript, generated % by hand or by many applications package. Virtually ALL of the power of % PostScript becomes available for your flutterwumper at near zero cost! % In use, ordinary PostScript code is combined with these routines and % sent to (preferably) Acrobat Distiller or (alternately) GhostScript % running on a host. A flutfile is generated. This file can be sent % out a port to immediately control the flutterwumper or can be kept % for later reuse. Any comm program can be used for an interface. % These routines are easily extended to input or output HPGL or Gerber files. % For most uses, these fancier formats are unnecessary complications. % Several example flutfiles are provided, including a simple box, a circle, % and a typographic character. Along with tolerance plots. % A flutfile reader is included. This can be used to proof or debug % flutfiles, or else to software emulate a hardware flutterwumper project % in development. % The fundamental core problems are to convert fonts into PostScript paths, % convert paths into straight line vectors and then convert those % vectors into individual flutfile steps. % Font path conversion gets done with the -charpath- operator. Curves % in the path get converted to straight line vectors using the -setflat- % and -flattenpath- operators. Paths are enumerated for vector conversion % with the -pathforall- operator. Step conversion uses the code below. % In this version, a vector flutfile system is used, sending commands for % octant moves of north, northwest, west, southwest, south, southeast, east, % and northeast. Note that a flutterwumper that INTERLEAVES x any y stepper % phases offers DRAMATICALLY smoother results, finer resolution, and shorter % file lengths. % All motions with the exception of the !H HOME command are RELATIVE with % respect to the CURRENT flutterwumper tool position. % Code at present is for 2-1/2 D applications, where the Z axis is a % simple up-down or on-off command. These routines are easily extended % for full three axis or higher uses. % While not included here, it is EXTREMELY SIMPLE to add transformations % for non-cartesian (non-XYZ) output devices. Hooks are provided. % These results have not yet been speed optimized. But they already are % much faster than most flutterwumpers, even at lower serial baud rates. % Table lookup (details on request) can offer speed improvements. % The code also provides "very good" but not "outstanding" typography. % On coarser resolution devices, slight retouching may be required, % compared to the simple and direct use of -charpath- shown here. % In any "low res" and "small letter" situation, picking a low res % friendly friendly font (Helvetica or Stone) can help bunches. It % is also possible to "retouch" the results by using the flutfile reader. % or custom hinting code. % Faster matrix transformation methods are also possible, but these % are harder to understand and debug. Plain old algebra is used instead. % //////////// (A) CREATE A POSTSCRIPT DICTIONARY ////////////////// % Since these tools intercept several fundamental PostScript commands, % they are best placed inside a closed dictionary until they are actually % required... /flutdict 100 dict def % define a dictionary flutdict begin % open dictionary for most following routines % ///////////// (B) A TIME DELAY ROUTINE ///////////////////////////// % A brief time delay might be handy for open loop flutterwumper homing, % special comm situations, and such. Here is a simple and obvious one... /stall1 {usertime add 300000 {usertime 1 index sub 0 ge {exit} if} repeat pop} def % Example: For a 600 millisecond delay, use 600 -stall1-. % ////////////// (C) OUTPUT FILE FORMATTER /////////////////////// % Flutterwumper files often consist of long strings of single characters. % This print formatter limits the width of certain printed lines... /linecount 0 def % initial character counter init /maxline 64 def % maximum linewidth /write1 { dup dup length 1 gt exch (\n) eq or {dest exch writestring /linecount 0 store} {dest exch writestring /linecount linecount 1 add store linecount maxline ge {dest (\n) writestring /linecount 0 store}if}ifelse} bind def /writecr1 {write1 (\n) dest exch writestring } def % ////////////// (D) OUTPUT COMMAND REDEFINER /////////////// % These hooks allow you to redefine your flutfile character set. % The ability to ignore everything between a "%" and a carriage % return or linefeed is assumed... % A flutterwumper that can INTERLEAVE X and Y step phases is assumed. % To convert these to degrees, MULTIPLY them by 45... /!E (0) def % step east /!NE (1) def % step northeast /!N (2) def % step north /!NW (3) def % step northwest /!W (4) def % step west /!SW (5) def % step southwest /!S (6) def % step south /!SE (7) def % step southeast /!H (:\n) def % step to XY home /!U (8\n) def % pen up command /!D (9\n) def % pen down command /!B (\n) def % initialize job /!Q (Q\n\n\n) def % job completed /!X (X\n) def % breaking debugger /!R {(R?) dup repeatcount 32 sub 1 put} def % repeatproc /repeatcount 4 def % and default /!cr (\n) def % forced carriage return /flutbegin {/lastxmove 0 def /lastymove 0 def /curxpos 0 def /curypos 0 def !B write1 !cr} def % send begin command /fluthome {flutpenup !H write1 !cr} def % home command /penupflag true def % initialize penup /flutpenup {penupflag not {!U write1 % pen up if not up /penupflag true store}if} def /flutpendown {penupflag {!D write1 % pen down if not down /penupflag false store}if} def /flutquit {flutpenup !Q write1 !cr} def % flut quit proc /flutbreak {flutpenup !X write1 !cr} def % flut break debugger /lastxmove 0 def % defaults /lastymove 0 def % //////////// (E) VECTOR OCTANT SELECTOR //////////////////// % Final stack has -x- -y- -angle- {octantproc}. /findoctant { 2 copy exch 2 copy 0 eq exch 0 eq and {pop 0.000001} if atan dup round cvi 45 idiv [{oct0}{oct1}{oct2}{oct3}{oct4}{oct5}{oct6}{oct7}{oct0}] exch get} def % examples: 10 4.7 leaves {oct0} on stack top; -4.7 10 leaves {oct2}. % //////////// (G) OCTANT VECTOR-TO-STEP ALGORITHMS ///////////// % Assume an octant #0 from 0 to +45 degrees. There will ALWAYS be more % X increments than Y in a step approximation. For each X step, % advance X, check the Y position. If half a unit low, also advance Y. % % These can later be speed optimized through table lookups of longer % vectors. They are presented in simple form for clarity here. /curxpos 0 def % running currentpoint for output /curypos 0 def % running currentpoint for output /oct0 { cos 0.5 mul /thresh exch store % calculate threshold 2 copy exch dup 0 eq {0.00001 add} if div % don't divide by zero /slope exch store % find the slope pop abs % don't need y no more? round cvi /#incs exch store % number of x incs 0 % set y increment counter 0 1 #incs 1 sub {/posn exch def % start loop /curxpos curxpos 1 add store % move one right posn 1 add slope mul thresh sub % test vertical position 1 index ge {1 add % y increment advance !NE write1 % command x and y move /curypos curypos 1 add store} % move one up {!E write1} % command x move only ifelse % go east or northeast? } for % for all x increments pop % done with y increment count } bind def % end proc % Each of the remaining octants is slightly different, rotating mirroring or % exchanging x for y... /oct1 {sin 0.5 mul /thresh exch store exch 2 copy exch div /slope exch store pop abs round cvi /#incs exch store 0 0 1 #incs 1 sub {/posn exch def /curypos curypos 1 add store posn 1 add slope mul thresh sub 1 index ge {1 add !NE write1 /curxpos curxpos 1 add store}{!N write1} ifelse } for pop} bind def /oct2 {sin 0.5 mul /thresh exch store exch 2 copy exch div neg /slope exch store pop abs round cvi /#incs exch store 0 0 1 #incs 1 sub {/posn exch def /curypos curypos 1 add store posn 1 add slope mul thresh sub 1 index ge {1 add !NW write1 /curxpos curxpos 1 sub store} {!N write1} ifelse } for pop} bind def /oct3 {cos -0.5 mul /thresh exch store 2 copy exch div neg /slope exch store pop abs round cvi /#incs exch store 0 0 1 #incs 1 sub {/posn exch def /curxpos curxpos 1 sub store posn 1 add slope mul thresh sub 1 index ge {1 add !NW write1 /curypos curypos 1 add store} {!W write1} ifelse } for pop} bind def /oct4 {cos -0.5 mul /thresh exch store 2 copy exch div /slope exch store pop abs round cvi /#incs exch store 0 0 1 #incs 1 sub {/posn exch def /curxpos curxpos 1 sub store posn 1 add slope mul thresh sub 1 index ge {1 add !SW write1 /curypos curypos 1 sub store}{!W write1} ifelse } for pop} bind def /oct5 {sin -0.5 mul /thresh exch store 2 copy div /slope exch store abs round cvi /#incs exch store pop 0 0 1 #incs 1 sub {/posn exch def /curypos curypos 1 sub store posn 1 add slope mul thresh sub 1 index ge {1 add !SW write1 /curxpos curxpos 1 sub store}{!S write1} ifelse } for pop} bind def /oct6 {sin -0.5 mul /thresh exch store 2 copy div neg /slope exch store abs round cvi /#incs exch store pop 0 0 1 #incs 1 sub {/posn exch def /curypos curypos 1 sub store posn 1 add slope mul thresh sub 1 index ge {1 add !SE write1 /curxpos curxpos 1 add store}{!S write1} ifelse } for pop} bind def /oct7 {cos 0.5 mul /thresh exch store 2 copy exch div neg /slope exch store pop abs round cvi /#incs exch store 0 0 1 #incs 1 sub {/posn exch def /curxpos curxpos 1 add store posn 1 add slope mul thresh sub 1 index ge {1 add !SE write1 /curypos curypos 1 sub store}{!E write1} ifelse } for pop} bind def % ////////////// (H) MAIN VECTOR-TO-STEP ALGORITHM ///////////////// % To convert a vector to individual XY steps, the octant is first found, % and then a suitable octant algorithm is used... /vectortostep {findoctant exec !cr write1 } bind def % ///////////// (I) FLATTENPATH CALCULATOR ////////////////////////// % For conversion to vectors, all curves get converted by -flattenpath-. % Too high a value, and output step size suffers. Too low a value and % the computation time get excessive. % Note that -flattenpath- is DEVICE DEPENDENT. A 2X cruder value is % needed at 600 DPI to get the same results at 300 DPI! /inch {72 mul} def % inch converter /flutres 0.01 inch def % DEVICE DEPENDENT output resolution % For small or complex typography, an oversample value of at least 8 % is recommended. This causes a -flattenpath- error of less than 0.25 % flutterwumper steps. Oversamples of 0.4 or less execute faster, but % make straight line approximations that have path errors.. % In general, this is a subtle variable. It does NOT affect the output file % length, but DOES affect computation time SOMEWHAT. The effects are data % dependent, but changes usually occur in large chunks. The average % flutfile line length gives you a clue whether a change has happened. % Leave -oversample- at 8 unless you are after special situations. /oversample 8 def % flat error per output resolution error % A scheme to find resolution of old level I PS code... /findres {save /snap exch def initgraphics matrix currentmatrix dup 0 get dup mul exch 1 get dup mul add sqrt 72 mul cvi snap restore} def /findflat {findres 1 exch div flutres 72 div exch div oversample div} def % /////////////// (J) CO-ORDINATE TRANSFORM AND SCALING /////////// % This does a custom scaling and optional nonlinear co-ordinate transform % from PostScript XY space to flutterwumper axis space. /coordxf {xfxyrelative} def % pick a set of rules % xfxyrelative is the default transform, shifting from absolute PS xy % values to relative flutterwumper values of suitable scale... % microsizing, XY home offsets, and tool diameter allowances are not yet % implemented. They can be added here... /xfxyrelative { curypos flutres mul sub flutres div exch % scale y to output res curxpos flutres mul sub flutres div exch % scale x to output res } def % /////////////// (K) PS PATH TO VECTOR CONVERTER ////////////////// % We will initially assume that ONLY paths ending with the -stroke- % operator OR strings ending with the -show- operator are to be vector % converted. We will also assume the default scale of one point. % At present, clips, fills, images, awidthshows, etc.. are IGNORED. % These features can easily be added as the need arises. % Define output resolution... /inch {72 mul} def /flutres 0.01 inch def % DEVICE DEPENDENT output resolution % Routines needed by -pathforall-... /lastxmove 0 def /lastymove 0 def /curxpos 0 def /curypos 0 def /movetoproc { flutpenup % pen or drill up coordxf % translate to flutter axes vectortostep % generate flutter file curypos flutres mul /lastymove exch store % save device yposn curxpos flutres mul /lastxmove exch store % save device xposn } def /linetoproc { flutpendown % pen or drill down coordxf % translate to flutter axes vectortostep % generate flutter file } def /curvetoproc { % should NOT happen! (Error - No curveto should remain!\n) write1 flush unflattenedcurvetoerror} def /closepathproc {lastxmove lastymove % force lineto linetoproc} def % Intercept PS commands for vector conversion. These MUST be in a dictionary % opened ONLY when generating flutterwumper files! /stroke {findflat setflat flattenpath {movetoproc}{linetoproc}{curvetoproc}{closepathproc} pathforall} def /show {false charpath stroke} def % //////////////// (L) CLOSE FLUTDICT DICTIONARY ////////////// end % place all above definitions into closed dictionary % ////////// (M) GENERATE TEST FILE - SIMPLE RECTANGLE ///////////// % The full flutdict definitions above are needed in the file or run % as a prefile before this code can be run. flutdict begin % open flutterwumper dictionary % Always use "\\" when you mean "\" in a PostScript filename string! /destfilename (a.out) def /inch {72 mul} def /flutres 0.01 inch def % set DEVICE DEPENDENT output resolution destfilename (w) file % create an output file /dest exch store % A flutfile permits a comment header. Eventually standards similar to % .eps file headers can be used. For now, just say something... ( %! flut3.0 Synergetics % WPS Story Teller, for Model 423 % $Id: flutools.ps,v 1.2 2003/03/11 03:41:24 tomj Exp $ ) writecr1 flutbegin % initialize flutterwumper fluthome % home flutterwumper to x=0 y=0 % ============= YOUR POSTSCRIPT SOURCE FILE STARTS HERE =============== % ============= MUST NOT INCLUDE SHOWPAGE, QUIT, ETC... =============== % ============== YOUR POSTSCRIPT SOURCE FILE ENDS HERE ================ % fluthome % send fluterwumper home % flutquit % eject job or clean up or whatever flush % MUST clear print buffer (!) dest closefile % MUST close output file end % MUST close flutproc dictionary (!) % /// (P) FLUTFILE ANALYZER/EMULATOR/READER/RETOUCHER/DEBUGGER ///////////// % (ALSO DISPENSES SOFT ICE CREAM) % /readflutfile reads a flutterwumper file and plots it back as PostScript. % This is useful for proof checks and to magnify closure and error details. % /readflutfile also can serve as an emulator and debugger during % flutterwumper hardware development. It may also be used to manually % "retouch" or otherwise "optimize" low resolution typography. % This version reads a disk based flutfile. Send it to the Acrobat % Distiller to generate a .PDF file. .PDF is recommended because of % its ease of magnification. % This does not yet include microsizing, tool allowances or home offset. % Repeat commands are not yet implimented 100 dict /flutreader exch def flutreader begin /xoffset 100 def % zero x position on page /yoffset 100 def % zero y position on page /upmode true def % initialize up-down mode /nocomment true def % initialize comment mode /inch {72 mul} def % inch scaling /flutres 0.01 inch def % current resolution of flutfile /penwidth 0.5 def % pen linewidth in points /showaxisflag true def % show 0-0 axis? /xaxislength 5 inch def % length of x axis /yaxislength 4.5 inch def % length of y axis /showgridflag true def % show a layout grid? /gridblock 0.1 inch def % small grid block size /gridblockx 1 inch def % large grid block size /black {0 0 0 setrgbcolor} def /green {0 1 0 setrgbcolor} def /red {1 0 0 setrgbcolor} def /blue {0 0 1 setrgbcolor} def systemdict /setstrokeadjust known {true setstrokeadjust} if % for video 256 array /flutcommandset exch def % build an array of all characters 0 1 255 {flutcommandset exch {} put} for % fill it with null procs (0) 0 get flutcommandset exch {nocomment {!E} if} put % stuff commands (1) 0 get flutcommandset exch {nocomment {!NE} if} put (2) 0 get flutcommandset exch {nocomment {!N} if} put (3) 0 get flutcommandset exch {nocomment {!NW} if} put (4) 0 get flutcommandset exch {nocomment {!W} if} put (5) 0 get flutcommandset exch {nocomment {!SW} if} put (6) 0 get flutcommandset exch {nocomment {!S} if} put (7) 0 get flutcommandset exch {nocomment {!SE} if} put (D) 0 get flutcommandset exch {nocomment {!D} if} put (U) 0 get flutcommandset exch {nocomment {!U} if} put (H) 0 get flutcommandset exch {nocomment {!H} if} put (B) 0 get flutcommandset exch {nocomment {!B} if} put (Q) 0 get flutcommandset exch {nocomment {!Q} if} put (%) 0 get flutcommandset exch {!com} put 10 flutcommandset exch {!ret} put % stuff control commands 13 flutcommandset exch {!ret} put % file insertable error trapper. Placing ! in the flutfile executes % custom code named debug. Like a trap or break... (!) 0 get flutcommandset exch {nocomment {debug}if} put % debugger % action commands... /!H {0 0 moveto} def % XY home startup /!com {/nocomment false def} def % start comment mode /!ret {/nocomment true def} def % reset comment mode /!D {/upmode false def} def % pen down /!U {/upmode true def} def % pen up /!go {upmode{rmoveto}{rlineto % action selector currentpoint stroke moveto}ifelse} def /!E {1 0 !go} def % go east one step /!NE {1 1 !go} def % go northeast one step /!N {0 1 !go} def % go north one step /!NW {-1 1 !go} def % go northwest one step /!W {-1 0 !go} def % go west one step /!SW {-1 -1 !go} def % go southwest one step /!S {0 -1 !go} def % go south one step /!SE {1 -1 !go} def % go southeast one step /!Q {} def % end of flutfile quit /!B {} def % flutterwumper setup % optional axis shower... /showaxis {showaxisflag {gsave green 1 setlinecap 2 setlinewidth gsave 0 0 moveto 0 yaxislength rlineto xaxislength 0 rlineto 0 yaxislength neg rlineto 0.92 1 0.92 setrgbcolor fill grestore 0 inch -0.1 inch moveto 0 yaxislength 0.2 inch add rlineto stroke -0.1 inch 0 moveto xaxislength 0.2 inch add 0 rlineto stroke grestore}if} def % optional fine grid shower... /showfinegrid {showgridflag{ gsave green 1 setlinewidth gsave xaxislength gridblock div 1 add cvi {0 0 moveto 0 yaxislength lineto stroke gridblock 0 translate} repeat grestore gsave yaxislength gridblock div 1 add cvi {0 0 moveto xaxislength 0 lineto stroke 0 gridblock translate} repeat grestore grestore }if}def % optional coarse grid shower... /showcoarsegrid {showgridflag{gsave green 2 setlinewidth gsave xaxislength gridblockx div 1 add cvi {0 0 moveto 0 yaxislength lineto stroke gridblockx 0 translate} repeat grestore gsave yaxislength gridblockx div 1 add cvi {0 0 moveto xaxislength 0 lineto stroke 0 gridblockx translate} repeat grestore grestore}if}def % this processes one flutfile line /procflutline {{flutcommandset exch get cvx exec} forall} def % this processes the entire flutfile /scanflutfile {black {source 128 string readstring {procflutline} {procflutline exit}ifelse} loop } def % an annotator... /graphtitle {gsave black xoffset yoffset translate /Helvetica-Bold findfont 12 scalefont setfont 0.1 inch -0.35 inch moveto show grestore} def % this does it all... /readflutfile { save /flutsnap exch store (r) file /source exch store % create read file xoffset yoffset translate % set initial page position showaxis % optionally show axis showfinegrid % optionally show layout grid showcoarsegrid penwidth setlinewidth % set pen width flutres dup scale % set flutres resolution % and magnification scanflutfile % extract flutfile commands flutsnap restore } def end % close flutreader dictionary %%%%%%%%%%%%%%%%%%%%%%% Further Comments %%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Note also that no matter what you do, running a small character of a fancy % font at low resolution will look less than stunning. But you can take % the stock Palatino "R" above, and "retouch" your flutfile. For instance, % the left foot can be flattened by replacing 0000000100 with 0000000000 and % 0000000070 with 0000000000. The small tic at the top inside of the R bowl % may be eliminated by replacing the 00001 and 0070 with 00000 and 0000. The % "byte" at the upper right can be improved by replacing the 33 44 43 with % 33 43 44, and so on. % With some practice, a flutfile can easily be "sight read". Try it. % Note that "%" comments may be inserted anywhere in a flutflie as markers % or reminders. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % GURU's LAIR VOICE HELPLINE % ========================== % % A no charge Guru's Lair voice helpline is avilable for your use % for technical information, referral to qualified consultants, and for % off-the-wall networking in general. % % Best calling times are 8-5 weekdays MOUNTAIN STANDARD TIME year around. % Please, US calls only. % % Phone or write your flutterwumper questions to: % % Don Lancaster % SYNERGETICS % Box 809-GL % Thatcher, AZ, 85552 % % (520) 428-4073 % % Or visit the www.tinaja.com website. % % Questions may also be emailed to don@tinaja.com % % Full consulting and PIC development services available. % A free TECHNICAL INSIDER SECRETS catalog is also available. % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % %%%%%%%%%%%%%%% END PS FLUTTERWUMPER UTILITIES %%%%%%%%%%%%%%%%%%%%%% % Consulting services available on concepts shown. % =================================================================== % Copyright c 1997 by Don Lancaster and Synergetics, Box 809, % Thatcher AZ, 85552. (520) 428-4073 www.tinaja.com don@tinaja.com % All commercial rights and all electronic media rights are % *fully* reserved. Linking welcome. Reposting expressly forbidden. % ===================================================================