[comp.lang.postscript] Color Spirograph

CXT105@psuvm.psu.edu (Christopher Tate) (10/30/90)

This is a repost of an earlier article, in which I posted my Spirograph
code (black & white version) to the net.  Since that time, I have added a
little color to the code -- it now does one complete cycle through the
HSB color model (starting and ending with red, and hitting every color
in the spectrum in between) when it draws the pictures.  The color change
is done as a smooth transition from one hue to the next as the drawing
progresses.

I've removed the explanation of the algorithm from this repost; if anyone
missed it the first time around, either (1) look for the recent article
entitled "Spirograph" posted by me, or (2) send me mail and I'll mail you
back a copy of the original explanation, without the code.  I'm doing this
to save wear and tear on the Usenet.  :-)

The actual change to the program is only one line (!), but just posting an
alteration ("To make Spirograph color, add this line at such-and-such a
place...") seemed a little awkward:  I forsaw myself getting a hundred
seperate notes of the pattern, "I missed your original Spirograph post, and
I'm interested in the color version, so could you please send it to me...?"
The demand for a color Spirograph program was truly overwhelming (and quite
flattering, too!), so here it is, in full.  Have fun, play with it, mess
with the colors; whatever strikes your fancy.  I'm sure you all have as
much free time as I do.... :-)

I will mention again, though, that this code contains a very well-behaved
implementation of Euclid's classic algorithm for calculating the Greatest
Common Divisor of two integers.  Anyone who's looking for such a beast,
feel free to borrow it!

-------
Christopher Tate                        |   "Oh wow:  not only is 57
                                        |    prime, but it's also
Bitnet: cxt105@psuvm                    |    divisible by three!"
Uucp: ...!psuvax1!psuvm.bitnet!cxt105   |
Internet: cxt105@psuvm.psu.edu          |    - a very sincere math major

--------------------------------- Cut Here ---------------------------------
%!PS-Adobe-1.0

%   This program generates a Spirograph(tm) image centered on the page
%   according to the three parameters bigR, littleR, and a.  Images are
%   generated by calculating successive positions of the cycloid generated
%   by rolling a small disk inside a larger ring, with a "pen" placed some
%   distance from the center of the disk.
%
%   The pen color is varied through the full spectrum as the image is
%   drawn, giving an interesting "fade" effect.  Combined with moire
%   effects and the effects of color overlap at the intersections of
%   differently colored segments of the curve, it's a pretty sight.
%
%   bigR is the radius of the "outer" circle (the ring, in Spirograph(tm))
%   littleR is the radius of the "inner" circle (the disk)
%   a is the distance of the "pen" from the center of the inner circle.
%
%   Since the program calculates the total angle through which to
%   parameterize (i.e. how long it has to roll the disk around in the
%   ring) by looking for a greatest common divisor, the values of the
%   parameters bigR and littleR MUST be integers.  a need not be integral,
%   since it is used merely as an offset from the center of the "disk."
%
%   If you're interested, the parametric equations which describe a
%   Spirograph(tm) image are given by:
%
%         x = (R - r) * cos(theta) + a * cos(phi)
%         y = (R - r) * sin(theta) + a * sin(phi),
%
%   where phi = ((r - R) / r) * theta.
%
%   This program was written by Christopher Tate, and is hereby placed
%   into the public domain.  Anyone may freely borrow from or modify
%   this program without permission.  Have fun!

/bigR 200 def         % just change these to define a new image
/littleR 169 def      % littleR should be less than bigR
/a 110 def            % a need not be smaller than littleR

% Euclid's Greatest Common Divisor algorithm to find gcd(m, n)
% Calling process:  m n *gcd* result

/gcd
{
   3 dict begin      % isolate these variables from the rest of the program
   /m exch def /n exch def
   /r m n mod def
   {
      r 0 eq {exit} if      % quit when r equals zero
      /m n def
      /n r def
      /r m n mod def
   } loop
   n                        % put result on the stack
   end                      % restore the original dictionary context
} def

clippath pathbbox      % find the bounding box of the page

/ury exch def          % remember Upper Right y
/urx exch def          % remember URx
/lly exch def          % remember LLy
/llx exch def          % remember LLx

urx llx sub 2 div llx add
ury lly sub 2 div lly add
translate              % move the origin to the center of the page

/diff bigR littleR sub def              % diff = R - r
/frac 1.0 bigR littleR div sub def      % frac = 1.0 - (R/r)

.08 setlinewidth           % sufficiently thin lines
newpath
diff a add 0 moveto        % set initial position

/limit 360 littleR mul bigR littleR gcd div def    % how far we need to go

0 3 limit
{
   dup frac mul /phi exch def       % calculate phi
   /theta exch def                  % recalculate theta
   theta limit div 1.0 1.0 sethsbcolor    % go through all colors
   theta cos diff mul phi cos a mul add   % this is x
   theta sin diff mul phi sin a mul add   % this is y
   2 copy lineto stroke moveto      % same as x y lineto stroke x y moveto
} for

showpage