[comp.windows.news] Jig-saw puzzle

leif@SUN.COM (Leif Samuelsson) (06/06/89)

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


#! /usr/NeWS/bin/psh
%
% 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