%!
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 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 (:) def % step to XY home
/!U (8) def % pen up command
/!D (9) def % pen down command
/!B (:) def % initialize job
/!Q (:) def % job completed
/!X (:) 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 (C:\\tomj\\projects\\xyzada~1\\XYZ.out) def
/inch {512 mul} def
/flutres 0.0003 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
% % XYZ Adapter test
% ) writecr1
flutbegin % initialize flutterwumper
fluthome % home flutterwumper to x=0 y=0
% Save as 5.0 compatible .EPS. Set origin to lower left. Set to 100 dpi
% resolution.
% ============= YOUR POSTSCRIPT SOURCE FILE STARTS HERE ===============
% ============= MUST NOT INCLUDE SHOWPAGE, QUIT, ETC... ===============