[comp.windows.news] NeWS Spirograph

zwicky@ptero.cis.ohio-state.edu (Elizabeth D. Zwicky) (02/04/88)

The program included here is a real spirograph program. I make
no claims about its clarity, and I know that at least two
changes are necessary to run it under NeWS 1.0 (they appear
at the beginning and are marked). I had been intending to clean
it up and comment it, and make some changes to the way that it
handles setting the vectors, but since I'm paid to manage and
write English, it looked like the year 2000 might come around
before I got that far.

A short note on the principles involved; a spirograph is basically
a rotating vector with a rotating vector on the end. In a spirograph
set, you can adjust three parameters; the length of the vectors, the
ratio of the speeds of the vectors, and whether they rotate in the
same direction or opposite directions. Since this virtual spirograph
does not have the limitations of plastic, you can change more parameters.
Notably, you can make the length of the vector proportional to the 
angle that it is at, thus changing the shape of the "wheel". You can
also change the speed of rotation according to the angle (this is 
"hyperweird mode"). Sorry there isn't more documentation.

	Elizabeth Zwicky (zwicky@ohio-state.arpa 
		          ...!cbosgd!osu-cis!tut!zwicky)

======================================================================
#! /usr/NeWS/bin/psh

% Menus with items that flip
/FlipMenu LitePullRightMenu dictbegin
	/AltMenuKeys null def
	/TempMenuKeys null def
	dictend
	classbegin
    /domenu { % menu => - (execute menu's action and flip executed item)
	 currentdict begin
	MenuValue getmenuaction dup type /dicttype eq {pop} {cvx exec} ifelse
	/newkey AltMenuKeys MenuValue get def 
	AltMenuKeys MenuValue MenuKeys MenuValue get put
	MenuItems  MenuValue get /Key newkey put 
	% for NeWS 1.0 MenuKeys MenuValue newkey put
	 end 
    } def
    /show { % Dunno why but placeholder seems to be needed
	/show super send
    } def
    /new { % 1 array of arrays and actions -> 3 arrays, 2 go up to super
	   dup 0 2 pickarray exch 1 2 pickarray
	    /LocalMenuActions exch def
	    /TempMenuKeys exch def
	   /StrippedKeys [ TempMenuKeys { {} forall} forall ] def
	    StrippedKeys 0 2 pickarray /LocalMenuKeys exch def
	    LocalMenuKeys LocalMenuActions /new super send begin
	    StrippedKeys 1 2 pickarray /AltMenuKeys exch store
	    currentdict end	
    } def
	classend def

% Menus with item last chosen starred
% Not tested under 1.0; if it fails, change CheckMenus to DefaultMenus

/CheckMenu LitePullRightMenu dictbegin dictend classbegin
	/domenu{
	0 1 MenuItems length 1 sub {MenuItems exch get /Key get dup 0 get  42 eq {0 32 put} if} for
	MenuValue getmenuaction dup type /dicttype eq {pop} {cvx exec} ifelse
	MenuItems MenuValue get /Key get dup 0 get dup 32 eq exch 42 eq or 
	{0 42 put} 
	{ dup /newkey  exch length 1 add string def newkey 0 42 put newkey
	 1 2 index putinterval MenuItems MenuValue get /Key newkey put} ifelse
	} store
	classend def

	 

% Initialization

/step 0 def % Counter for inner cycle

/dot{0 0 rlineto}def 

/around 360 def % number of degrees in a full cycle; some functions require
% more than 360 degrees to repeat

/stepfix{/hold step midstep mul def /fracpart hold hold
truncate sub def hold cvi around mod fracpart add} def % Converts step counter
% to the angle the inner vector is at

/outstepfix{/hold step angle mul def /fracpart hold hold truncate sub
def hold cvi around mod fracpart add} def % Converts step counter to the
% angle the inner vector is at

% Original Settings

/midstep .5 def % The number of degrees the innermost vector rotates
% by per dot

/12ratio .4 def % The ratio between the speeds of the first two vectors

/len 150 def % The length of the inner vector

/len2 90 def % The length of the middle vector

/rev{true}def % vectors rotate same (false) or different (true) directions

/innerfun{sin}def 
/outerfun{cos}def
/len1fun{/side1 stepfix 45 60 polygon def} def
/len2fun{/side2 len2 def}def
/weird false def
/flip false def
/weird2  false def
/double false def
/tracein false def 
/outweird false def
/outflip false def
/side1 150 def
/indouble false def

% Calculations before spiro 
/calc{/angle 12ratio midstep mul store
	/timesaround 12ratio def
	12ratio truncate 12ratio sub 0 ne 
	{/fracangle 12ratio 12ratio truncate sub def
	/try1 1 fracangle div def
	/timesaround 10 def
	try1 truncate try1 sub 0 eq {/timesaround try1 def} if
	fracangle 10 mul round 2 mod 0 eq {/timesaround 5 def}if} if
	/useta timesaround around mul midstep div store}def
/angle{} def

/polygon {/center exch def /mnum exch def /thisstep exch def /foo thisstep
	 mnum div def /test foo pause
	truncate def /frac foo test sub def /side  mnum tan center mul def
	test 2 mod 0 eq {/place frac def}{/place 1 frac sub def} ifelse
	center 2 exp place side mul 2 exp add sqrt pause} def
/omega 3 def
/outomega 3 def
/squarewave{/thisstep exch def /mult exch def 0 1 2 25 {/n exch def n mult mul thisstep 
	 mul sin n div add pause } for} def
/sawtooth{/thisstep exch def /mult exch def 0 1 2 25 {/n exch def n mult mul thisstep
	 mul cos n div add n 1 add mult mul thisstep mul sin n 1 add div sub pause} 
	for} def
/edz{/thisstep exch def /mult exch def 0 1 4 29 {/n exch def n mult mul thisstep mul
	cos n cos div add pause} for} def

% Draw a spirograph
 /spiro{/step 1 def /rotation 1 def
	{/midvary step 360 mod  cos 1 add midstep mul def pause
	len2fun pause
         len1fun pause
	 weird {side1 0 eq {flip {/flip false store}{/flip true store} ifelse} if}if 
	outweird {side2 0 eq {outflip {/outflip false store}{/outflip true store}ifelse}if}if
	 flip {/side1 side1 neg store} if
	outflip {/side2 side2 neg store} if
	gsave
	side1 0 rmoveto tracein {dot} if pause
	rev {angle step mul neg rotate}
             {angle step mul rotate} ifelse
         side2 0 rmoveto
         tracein not {dot} if pause
	double {side2 neg 2 mul 0 rmoveto tracein not {dot}if } if
        stroke grestore
	indouble {gsave side1 neg 0 rmoveto tracein {dot} if
		rev {angle step mul neg rotate}
			{angle step mul rotate} ifelse
		side2 0 rmoveto tracein not {dot} if
		double {side2 neg 2 mul 0 rmoveto tracein not {dot}if } if
		stroke pause grestore } if
	 weird2 {midvary rotate}{midstep rotate} ifelse
	
         /step step 1 add def pause
	/rotation rotation  midvary add def
        stroke  0 0 moveto
        useta step  eq {exit} if
        }loop
      stroke}def

/len1 150 def /len2 150 def

/fun1mulvalue 2 def
/outfun1mulvalue 4 def
/fun1value{(sin)}def
/outfun1value{(sin)}def
/fun2mulvalue 2 def
/outfun2mulvalue 2 def
/fun2value (sin) def
/outfun2value(sin)def
/relationvalue (add) def
/outrelationvalue (add) def
/div*{dup 0 eq {0}{div }ifelse}def
/tan*{dup 90 eq {0}{tan} ifelse}def
/lenfun{/side1 stepfix fun1mulvalue
	mul fun1value cvx exec  len1 mul def}def
/outlenfun{/side2 outstepfix  outfun1mulvalue mul outfun1value cvx exec
	len2 mul def}def


/main{/win framebuffer /new DefaultWindow send def
/reshapefromuser win send

4 (Zap!) {win /PaintProcess get null ne{win /PaintProcess  get killprocessgroup} if win /FrameEventMgr get null ne
{win /FrameEventMgr get killprocessgroup} if} /changeitem win /FrameMenu get send

/set12ratiofrommenu {/12ratio currentkey cvr store} def

/12RatioMenu [(.1)(.2)(.3)(.4)(.5)(.6)(.7)(.8)(.9)(1)(1.1)(1.2)(1.3)(1.4)(1.5)(1.6)(1.7)(1.8)(1.9)(2)][{set12ratiofrommenu}]
/new CheckMenu send def


/MidstepMenu[(.25)(.5)(.75)(1)(1.25)]
	 [{/midstep currentkey cvr store}] /new DefaultMenu send def

/sillyarray 
	{(Inner Flip On)(Inner Flip Off)}{weird {/weird false store}{/weird 
		true store} ifelse}
	{(Outer Flip On)(Outer Flip Off)}{outweird {/outweird false store}
		{/outweird true store}ifelse}
	{(Inner Double On)(Inner Double Off)}{indouble {/indouble false store}
		{/indouble true store} ifelse}
	{(Outer Double On)(Outer Double Off)}{double {/double false store}{/double true 
		store}ifelse}
	{(Trace Inner Vector)(Show Whole Design)}{tracein {/tracein false 
		store }	{/tracein true store} ifelse}
	{(HyperWeird Mode On)(HyperWeird Mode Off)} {weird2 {/weird2 false 
		store} {/weird2 true store} ifelse}
12 array astore def /WeirdMenu 
sillyarray /new FlipMenu send
def

/InSerMulMenu  [(2)(3)(4)(5)(6)(7)(8)(9)] [{/omega currentkey cvr store}] /new DefaultMenu send def

/OutSerMulMenu  [(2)(3)(4)(5)(6)(7)(8)(9)] [{/outomega currentkey cvr store}]  /new DefaultMenu send def

/InSeriesMenu [(Multiplier =>) InSerMulMenu (Square Wave) {/len1fun {/side1
	omega  stepfix squarewave 150
	mul def} store}	 (Sawtooth) {/len1fun {/side1 omega stepfix sawtooth
	150 mul def} store} (Today's Special) {/len1fun {/side1
	omega stepfix edz 150 mul def} store}] /new DefaultMenu send def

/OutSeriesMenu [(Multiplier =>) OutSerMulMenu (Square Wave) {/len2fun {/side2
	outomega outstepfix squarewave
	150 mul def} store} (Sawtooth) {/len2fun {/side2 outomega outstepfix
	sawtooth 150 mul def} store} (Today's Special) {/len2fun
	{/side2 outomega outstepfix edz 50 mul def} store}] /new DefaultMenu send def

/InPolyMenu [(3)(4)(5)(6)(7)(8)(9)(10)] [{/insides 90 180 360 currentkey cvi div sub 
	2 div sub store /len1fun 
	{/side1 stepfix insides 60 polygon def} store}] /new DefaultMenu send def

/OutPolyMenu [(3)(4)(5)(6)(7)(8)(9)(10)] [{/outsides 90 180 360 currentkey cvi div sub
	2 div sub store /len2fun
	 {/side2 outstepfix outsides 60 polygon def} store}]/new DefaultMenu send def

/outsides{}def /insides{}def
/InFunMenu [(Sine){/fun1value (sin) store} (Cosine) {/fun1value (cos) store}
	(Tangent){/fun1value (tan*) store}]/new DefaultMenu send def

/OutFunMenu [(Sine){/outfun1value (sin) store} (Cosine) {/outfun1value (cos) store}
	(Tangent) {/outfun1value (tan*) store}] /new DefaultMenu send def

/InMulMenu  [(2)(3)(4)(5)(6)(7)(8)(9)] [{/fun1mulvalue currentkey cvr store}] /new DefaultMenu send def

/OutMulMenu  [(2)(3)(4)(5)(6)(7)(8)(9)] [{/outfun1mulvalue currentkey cvr store}]  /new DefaultMenu send def

/donothing {} def

/InRelMenu [(Add) {/relationvalue (add) store} (Subtract) {/relationvalue (sub) 
	store} (Multiply) {/relationvalue (mul) store} (Divide) {/relationvalue
	(div*) store}] /new DefaultMenu send def

/OutRelMenu [(Add) {/outrelationvalue (add) store} (Subtract) {/outrelationvalue
	 (sub) store} (Multiply) {/outrelationvalue (mul) store} (Divide) 
	{/outrelationvalue (div*) store}] /new DefaultMenu send def

/InFun2Menu [(Sine){/fun2value (sin) store} (Cosine) {/fun2value (cos) store}
	(Tangent){/fun2value (tan*) store}]/new DefaultMenu send def

/OutFun2Menu [(Sine){/outfun2value (sin) store} (Cosine) {/outfun2value (cos) store}
	(Tangent){/outfun2value (tan*) store}]/new DefaultMenu send def

/InMul2Menu  [(2)(3)(4)(5)(6)(7)(8)(9)] [{/fun2mulvalue currentkey cvr store}] 
	/new DefaultMenu send def

/OutMul2Menu  [(2)(3)(4)(5)(6)(7)(8)(9)] [{/outfun2mulvalue currentkey cvr store}]
	  /new DefaultMenu send def

/InSecMenu [(Relation =>) InRelMenu (Function =>) InFun2Menu (Multiplier =>) 
	InMul2Menu (Set) {/lenfun {/side1 stepfix fun1mulvalue mul fun1value
	cvx exec stepfix fun2mulvalue mul fun2value cvx exec relationvalue cvx
	exec len1 mul def} store}] /new DefaultMenu send def

/OutSecMenu [(Relation =>) OutRelMenu (Function =>) OutFun2Menu (Multiplier =>) 
	OutMul2Menu (Set) {/outlenfun {/side2 outstepfix outfun1mulvalue mul
	 outfun1value cvx exec outstepfix outfun2mulvalue mul outfun2value
	 cvx exec outrelationvalue cvx
	exec len2 mul def} store}] /new DefaultMenu send def


/InCyclicMenu [(Function =>) InFunMenu (Multiplier =>) InMulMenu (Second Cycle =>) 
	InSecMenu (Set) {/len1 80 store /len1fun {lenfun} store}] /new DefaultMenu send def

/OutCyclicMenu [(Function =>) OutFunMenu (Multiplier =>) OutMulMenu
	 (Second Cycle =>) OutSecMenu (Set) {/len2 80 store 
	/len2fun {outlenfun} store}]
	 /new DefaultMenu send def

/incircle{infirsttime {/inshowwin framebuffer /new DefaultWindow send store
		 /reshapefromuser inshowwin send
		{4 (Zap!){inshowwin /IconCanvas get /Mapped false put
			inshowwin /FrameCanvas get /Mapped false put
			inshowwin /ClientCanvas get /Mapped false put
			inshowwin /FrameEventMgr get killprocess
			/infirsttime true store} 
		/changeitem FrameMenu send
		/PaintClient {clippath pathbbox /y exch def /x exch def
		erasepage x 2 div y 2 div translate /dot{0 0 rlineto} def
				 /step 0 def 0 0 moveto
				720  {len1fun
					side1 0 rmoveto
					dot pause stroke
					0 0 moveto
					1 rotate
					/step step 1 add def} repeat
		 stroke} def
		/FrameLabel (Inner Vector Function Graph) def}
		inshowwin send 
		/map inshowwin send /infirsttime false store}
		{/paintclient inshowwin send}ifelse}def
		/infirsttime true def

/outcircle{outfirsttime {/outshowwin framebuffer /new DefaultWindow send store
		 /reshapefromuser outshowwin send
		{4 (Zap!){outshowwin /IconCanvas get /Mapped false put
			outshowwin /FrameCanvas get /Mapped false put
			outshowwin /ClientCanvas get /Mapped false put
			outshowwin /FrameEventMgr get killprocess
			/outfirsttime true store} 
		/changeitem FrameMenu send
	/outstepfix {step} def
		/PaintClient {clippath pathbbox /y exch def /x exch def
		erasepage x 2 div y 2 div translate /dot{0 0 rlineto} def
				 /step 0 def 0 0 moveto
				720  {len2fun
					side2 0 rmoveto
					dot pause stroke
					0 0 moveto
					1 rotate
					/step step 1 add def} repeat
		 stroke} def
		/FrameLabel (Outer Vector Function Graph) def}
		outshowwin send 
		/map outshowwin send /outfirsttime false store}
		{/paintclient outshowwin send}ifelse}def
		/outfirsttime true def

/outstraight{outstraightfirsttime {/outstraightshowwin framebuffer /new DefaultWindow send store
		 /reshapefromuser outstraightshowwin send
		{4 (Zap!){outstraightshowwin /IconCanvas get /Mapped false put
			outstraightshowwin /FrameCanvas get /Mapped false put
			outstraightshowwin /ClientCanvas get /Mapped false put
			outstraightshowwin /FrameEventMgr get killprocess
			/outstraightfirsttime true store} 
		/changeitem FrameMenu send
		/outstepfix{step} def
		/PaintClient {erasepage 0 100 translate /dot{0 0 rlineto} def
				.5 .5 scale /step 0 def 0 0 moveto
				3600  {len2fun step 100 moveto
					0 side2 rmoveto
					dot pause stroke
					/step step 1 add def} repeat
		 stroke} def
		/FrameLabel (Outer Vector Function Graph) def}
		outstraightshowwin send 
		/map outstraightshowwin send /outstraightfirsttime false store}
		{/paintclient outstraightshowwin send}ifelse}def
		/outstraightfirsttime true def
/instraightshowwin{}def
/outstraightshowwin{}def
/inshowwin{}def
/outshowwin{}def
/instraight{instraightfirsttime {/instraightshowwin framebuffer /new DefaultWindow send store
		 /reshapefromuser instraightshowwin send
		{4 (Zap!){instraightshowwin /IconCanvas get /Mapped false put
			instraightshowwin /FrameCanvas get /Mapped false put
			instraightshowwin /ClientCanvas get /Mapped false put
			instraightshowwin /FrameEventMgr get killprocess
			/instraightfirsttime true store} 
		/changeitem FrameMenu send
		/PaintClient {erasepage 0 100 translate /dot{0 0 rlineto} def
				.5 .5 scale /step 0 def 0 0 moveto
				3600  {len1fun step 100 moveto
					0 side1 rmoveto
					dot pause stroke
					/step step 1 add def} repeat
		 stroke} def
		/FrameLabel (Inner Vector Function Graph) def}
		instraightshowwin send 
		/map instraightshowwin send /instraightfirsttime false store}
		{/paintclient instraightshowwin send}ifelse}def
		/instraightfirsttime true def

/PlotMenu [(Inner Vector Straight) /instraight (Inner Vector Circular) /incircle
	(Outer Vector Straight) 
	/outstraight (Outer Vector Circular) /outcircle ] /new DefaultMenu send def

/InAngleMenu [(Inner Angle, Adjusted) {/stepfix{/hold step midstep mul def
	 /fracpart hold hold truncate sub def hold cvi around mod fracpart add} 
	store }(Inner Angle, Theoretical) {/stepfix{/hold step def
	 /fracpart hold hold truncate sub def hold cvi around mod fracpart add} 
	store} (Outer Angle, Adjusted) {/stepfix{/hold step angle mul def
	 /fracpart hold hold truncate sub def hold cvi around mod fracpart add}
	store } (Outer Angle, Theoretical){/stepfix{/hold step 12ratio mul def
	 /fracpart hold hold truncate sub def hold cvi around mod fracpart add} 
	store}] /new DefaultMenu send def

/OutAngleMenu[(Outer Angle, Adjusted){/outstepfix{/hold step angle mul def
	 /fracpart hold hold truncate sub def hold cvi around mod fracpart add} 
	store} (Outer Angle, Theoretical){/outstepfix{/hold step 12ratio mul def
	 /fracpart hold hold truncate sub def hold cvi around mod fracpart add} 
	store} (Inner Angle, Adjusted) {/outstepfix{/hold step midstep mul def
	 /fracpart hold hold truncate sub def hold cvi around mod fracpart add} 
	store}(Inner Angle, Theoretical)  {/outstepfix{/hold step def
	 /fracpart hold hold truncate sub def hold cvi around mod fracpart add} 
	store}] /new DefaultMenu send def

/InnerMenu [(Angle =>) InAngleMenu (Series =>) InSeriesMenu (Polygon =>) InPolyMenu (Circle) {/len1fun
	 {/side1 150 def} def} (Cyclic =>) InCyclicMenu] /new DefaultMenu send def

/OuterMenu [(Angle =>) OutAngleMenu (Series =>) OutSeriesMenu (Polygon =>) OutPolyMenu (Circle) {/len2fun {/side2 150 def} def}
	 (Cyclic =>) OutCyclicMenu] /new DefaultMenu send def


/FiddleMenu [(Flip Reverse){rev {/rev{false}store} {/rev{true}store} ifelse}
(Set 12Ratio =>)12RatioMenu (Midstep =>)MidstepMenu (Weirdness =>)WeirdMenu
(Plot =>)PlotMenu (Inner Vector =>)InnerMenu (Outer Vector =>) OuterMenu]
/new DefaultMenu send def

{/PaintClient{clippath pathbbox /tmpy exch def /tmpx exch def tmpx 600
div tmpy 600 div scale pop pop erasepage 300 300 translate 0 0 moveto
calc spiro}def

/ClientMenu [(Fiddle Parameters)FiddleMenu(Redraw){/paintclient win send}]
	/new DefaultMenu send def
/FrameLabel (Spirograph) def}win send
/map win send} def
main