#! /usr/NeWS/bin/psh
%
% Date: Mon, 5 Jun 89 18:14:37 EDT
% To: NeWS-makers@brillig.umd.edu
% Subject: Jig-saw puzzle
% From: leif@Sun.COM (Leif Samuelsson)
% 
% I stumbled on this old demo and realized that it probably
% never made it out to the world.  It still needs some fine tuning
% if anybody is interested. Enjoy!
% 
% Leif Samuelsson			leif@sun.com
% Sun Microsystems, Inc
% 
% puzzle
%
% Puzzle is an interactive jig-saw puzzle program.
% The pieces can be moved around with the middle mouse
% button. The menu contains a "solve" command and also
% allows for changing image in mid-game.
%
% 890604  Made public domain - posted to Usenet
% 871022  Fixed bugs - works with NeWS 1.1
% 870601  Rewrote using o.o.p.
% 870416  Made it work with NeWS 1.0
% 870127  First release
%
% Author: Leif Samuelsson, Sun Microsystems, Inc.

8 setretainthreshold

/NROWS 4 def
/NCOLS { NROWS } def
/NPIECES { NROWS NCOLS mul } def
/BACKGROUND .9 def

% Setting curve to false causes the pieces to be rectangular
% instead of interlocking. This speeds up the program considerably.
/curve false def

/imagedirectory (/usr/NeWS/smi/) def

/fileextension (.im8) def

/picfile (man) def
/picimage
	currentcanvas newcanvas
	dup makecanvasopaque
	dup makecanvasretained
def

/readimage {
    gsave
	0 0 moveto
	piecewidth NCOLS mul pieceheight NROWS mul rect
	picimage
	dup reshapecanvas
	setcanvas
	imagedirectory picfile append
	fileextension append
	readcanvas
	wscale hscale scale
	imagecanvas
    grestore
} def

% Define a class "Piece" which knows how to draw
% and move itself.
%
/Piece Object [/Row /Column /TheCanvas /EventMgr /Path] classbegin
    /new {			% Row Column parentcanvas => inst.
	/new super send begin
	    /TheCanvas exch newcanvas store
	    /Column exch store
	    /Row exch store
	    gsave
		initmatrix
		TheCanvas
		dup makecanvasopaque
		dup mapcanvas
		dup makecanvasretained
		random piecewidth NCOLS 3 mul 2 div 1 sub mul mul
		random pieceheight NROWS 3 mul 2 div 1 sub mul mul
		/xarc 48 def
		translate
		0 0  moveto
		Row 0 ne curve and
		    { piecewidth 2 div
		      pieceheight 8 div
		      pieceheight 6 div
		      180 xarc add
		      360 xarc sub
		      arcn
		    } if
		piecewidth 0 lineto
		Column NCOLS 1 sub ne curve and
		    { piecewidth dup 8 div add
		      pieceheight 2 div
		      piecewidth 6 div
		      270 xarc sub
		      90 xarc add
		      arc
		    } if
		piecewidth pieceheight lineto
		Row NROWS 1 sub ne curve and
		    { piecewidth 2 div
		      pieceheight dup 8 div add
		      pieceheight 6 div
		      360 xarc sub
		      180 xarc add
		      arc
		    } if
		0 pieceheight lineto
		Column 0 ne curve and
		    { piecewidth 8 div
		      pieceheight 2 div
		      piecewidth 6 div
		      90 xarc add
		      270 xarc sub
		      arcn
		    } if
		closepath
		/Path currentpath store
		reshapecanvas
	    grestore
	    /EventMgr [
		    currentdict			% piece
		    TheCanvas			% piece can
		    MiddleMouseButton		% piece can name
			[ /move			% piece can name [ /move
			5 -1 roll		% can name [ /move piece
			/send cvx ] cvx		% can name proc
			DownTransition		% can name proc action
			4 -1 roll		% name proc action can
			eventmgrinterest
	    ] forkeventmgr store
	    currentdict
	end
    } def

    /killeventmgr {
	EventMgr /EventMgr null store killprocess
    } def

    /paint {
	TheCanvas setcanvas
	gsave
	    Column piecewidth mul neg
	    Row pieceheight mul neg
	    translate
	    wscale hscale scale
	    picimage imagecanvas
	grestore
%	0 strokecanvas
    } def

    /slidehome {
	{
	    can setcanvas
	    1 1 22 {
		gsave
		    TheCanvas getcanvaslocation		% x y
		    TheCanvas setcanvas
		    Column .5 add piecewidth mul
		    Row .5 add pieceheight mul		% x y x' y'
		    3 -1 roll				% x x' y' y
		    dup add add 3 div round		% x x' y"
		    3 1 roll exch			% y" x' x
		    dup add add 3 div round		% y" x"
		    exch
		    movecanvas
		grestore
		pause
	    } for
	    TheCanvas setcanvas
	    Column .5 add piecewidth mul
	    Row .5 add pieceheight mul
	    movecanvas
	} fork
    } def

    /center {
	TheCanvas setcanvas
	wscale 2 div hscale 2 div  movecanvas
    } def

    /qsolve {
	TheCanvas setcanvas
	Column .5 add piecewidth mul
	Row .5 add pieceheight mul
	movecanvas
    } def

    /randomize {
	TheCanvas setcanvas
	random piecewidth NCOLS 3 mul 2 div 1 sub mul mul
	random pieceheight NROWS 3 mul 2 div 1 sub mul mul
	movecanvas
    } def

    /move {
	15 dict begin
	    /xo null def /yo null def
	    gsave
		    can setcanvas
		    TheCanvas getcanvaslocation		% x y
		    initmatrix
		    can setcanvas
		    currentcursorlocation		% x y x' y'
		    3 -1 roll				% x x' y' y
		    sub /yo exch store
		    exch sub /xo exch store
		    BACKGROUND setgray
		    0 0 {
			gsave
			    TheCanvas setcanvas
			    yo sub exch xo sub exch
			    movecanvas
			grestore
		    } xgetanimated waitprocess aload pop
	    grestore
	end
    } def
classend def		% End of class "Piece"

/Pieces NPIECES array def

/piecewidth 0 def
/pieceheight 0 def

/wscale { piecewidth NCOLS mul } def
/hscale { pieceheight NROWS mul } def

/restart {
    Pieces { /killeventmgr exch send } forall
    /Pieces NPIECES array store		% This causes g.c. of old pieces
    pause
    newpieces
    paintpieces
    /paint win send
} def

/xgetanimated {
    10 dict begin
    /proc exch  def /y0 exch def /x0 exch def
    currentcursorlocation /y exch def /x exch def
    GA_constraint null ne GA_value null eq and {
	/GA_value currentcursorlocation GA_constraint 1 eq {exch} if pop store
    } if
    {	createevent dup begin
	  /Action [UpTransition DownTransition] def
	  end expressinterest
	createevent dup /Name /MouseDragged put expressinterest
	{   
	    GA_constraint 0 eq {/x GA_value def} if
	    GA_constraint 1 eq {/y GA_value def} if
	    x0 y0 moveto x y /proc load exec
	    awaitevent begin
	      Action UpTransition eq { end exit } if
	      /x XLocation store /y YLocation store
	    end
	} loop
	/GA_constraint null store
	/GA_value null store
	[x y] 
    } fork
    end
} def

/newpieces {			% - newpiece piece
    2 dict begin
	can setcanvas
	clippath pathbbox
	    /pieceheight exch 2 mul 3 div NROWS div round store
	    /piecewidth exch 2 mul 3 div NCOLS div round store
	    pop pop
	readimage
	0 1 NROWS 1 sub {
	    /i exch def
	    0 1 NCOLS 1 sub {
		/j exch def
		i j can /new Piece send
		Pieces exch i NCOLS mul j add exch put
	    } for
	} for
    end
}def

/paintpieces {
    Pieces {/paint exch send} forall
} def

/menuselect {
    /picfile MenuKeys MenuValue get store
    readimage
    paintpieces
} def

/davincipicturemenu [
    (angel)
    (ermine)
    (lady)
    (man)
    (mona-face)
    (mona-hands)
    (mona-smile)
%    (mona-smile-hires)
    (mona)
    (stjerome)
    (virgin)
    (virginofrocks)
] [{ menuselect }] /new DefaultMenu send def

/japanesepicturemenu [
    (cherries)
    (fuji)
    (geese)
    (puppet)
    (snow)
    (stormy)
    (washing)
%    (washing-hires)
    (writing)
] [{ menuselect }] /new DefaultMenu send def

/sunpicturemenu [
    (founders)
    (sun3110)
    (sun3160c)
    (sun3160m)
    (sun3260h)
    (sun350)
    (sun352)
    (sun352w)
    (sunballs)
    (suncase)
    (sungame)
    (sunnet)
    (sunnfs)
    (sunprism)
] [{ menuselect }] /new DefaultMenu send def

/travelpicturemenu [
    (bryce)
    (harem)
    (joshua)
    (lascruces)
    (new_york)
    (pagosa)
    (saturn)
    (shroom)
    (taj-detail)
    (taj)
    (veggies)
    (zion)
] [{ menuselect }] /new DefaultMenu send def

/winpicturemenu [
    (Da Vinci =>)	davincipicturemenu
    (Japanese =>)	japanesepicturemenu
    (Sun =>)		sunpicturemenu
    (Misc =>)	travelpicturemenu

] /new DefaultMenu send def

/piecesmenu
    [ (1) (4) (9) (16) (25) (36) (49) (64)]
    [{ /NROWS MenuValue 1 add store restart }]
    /new DefaultMenu send def

/win framebuffer /new DefaultWindow send def
{
	/FrameLabel (Puzzle) def
	/PaintClient { 
		ClientCanvas setcanvas
		BACKGROUND fillcanvas
	} def
	/PaintIcon {
	    gsave
		IconCanvas setcanvas
		clippath pathbbox scale pop pop
		picimage imagecanvas
		0 strokecanvas
	    grestore
	} def
	/ClientMenu [
		(Solve)			{ Pieces { /slidehome exch send } forall }
		(Quick Solve)		{ Pieces { /qsolve exch send } forall }
		(Scatter Pieces)	{ Pieces { /randomize exch send } forall }
		(Stack Pieces)		{ Pieces { /center exch send } forall }
		(Interlock On/Off) 	{ /curve curve not store restart } 
		(Picture =>)		winpicturemenu
		(No. of Pieces =>)	piecesmenu
		(Zap)			{currentprocess killprocessgroup}
	] /new DefaultMenu send def
} win send

/reshapefromuser win send
/map win send
/can win /ClientCanvas get def
/paint win send
pause
newpieces
paintpieces
