In the long run, every program becomes rococo, and then rubble.
Alan Perlis
By now, we know how to present arbitrary `Content-type's to a browser.
In this section, our server will present a 3D world to our browser.
The 3D world is described in a scene description language (VRML,
Virtual Reality Modeling Language) that allows us to travel through a
perspective view of a 2D maze with our browser. Browsers with a
VRML plugin enable exploration of this technology. We could do
one of those boring `Hello world' examples here, that are usually
presented when introducing novices to
VRML. If you have never written
any VRML code, have a look at
the VRML FAQ.
Presenting a static VRML scene is a bit trivial; in order to expose
gawk's new capabilities, we will present a dynamically generated
VRML scene. The function SetUpServer
is very simple because it
only sets the default HTML page and initializes the random number
generator. As usual, the surrounding server lets you browse the maze.
function SetUpServer() { TopHeader = "<HTML><title>Walk through a maze</title>" TopDoc = "\ <h2>Please choose one of the following actions:</h2>\ <UL>\ <LI><A HREF=" MyPrefix "/AboutServer>About this server</A>\ <LI><A HREF=" MyPrefix "/VRMLtest>Watch a simple VRML scene</A>\ </UL>" TopFooter = "</HTML>" srand() }
The function HandleGET
is a bit longer because it first computes
the maze and afterwards generates the VRML code that is sent across
the network. As shown in the STATIST example
(see STATIST),
we set the type of the
content to VRML and then store the VRML representation of the maze as the
page content. We assume that the maze is stored in a 2D array. Initially,
the maze consists of walls only. Then, we add an entry and an exit to the
maze and let the rest of the work be done by the function MakeMaze
.
Now, only the wall fields are left in the maze. By iterating over the these
fields, we generate one line of VRML code for each wall field.
function HandleGET() { if (MENU[2] == "AboutServer") { Document = "If your browser has a VRML 2 plugin,\ this server shows you a simple VRML scene." } else if (MENU[2] == "VRMLtest") { XSIZE = YSIZE = 11 # initially, everything is wall for (y = 0; y < YSIZE; y++) for (x = 0; x < XSIZE; x++) Maze[x, y] = "#" delete Maze[0, 1] # entry is not wall delete Maze[XSIZE-1, YSIZE-2] # exit is not wall MakeMaze(1, 1) Document = "\ #VRML V2.0 utf8\n\ Group {\n\ children [\n\ PointLight {\n\ ambientIntensity 0.2\n\ color 0.7 0.7 0.7\n\ location 0.0 8.0 10.0\n\ }\n\ DEF B1 Background {\n\ skyColor [0 0 0, 1.0 1.0 1.0 ]\n\ skyAngle 1.6\n\ groundColor [1 1 1, 0.8 0.8 0.8, 0.2 0.2 0.2 ]\n\ groundAngle [ 1.2 1.57 ]\n\ }\n\ DEF Wall Shape {\n\ geometry Box {size 1 1 1}\n\ appearance Appearance { material Material { diffuseColor 0 0 1 } }\n\ }\n\ DEF Entry Viewpoint {\n\ position 0.5 1.0 5.0\n\ orientation 0.0 0.0 -1.0 0.52\n\ }\n" for (i in Maze) { split(i, t, SUBSEP) Document = Document " Transform { translation " Document = Document t[1] " 0 -" t[2] " children USE Wall }\n" } Document = Document " ] # end of group for world\n}" Reason = "OK" ORS "Content-type: model/vrml" Header = Footer = "" } }
Finally, we have a look at MakeMaze
, the function that generates
the Maze
array. When entered, this function assumes that the array
has been initialized so that each element represents a wall element and
the maze is initially full of wall elements. Only the entrance and the exit
of the maze should have been left free. The parameters of the function tell
us which element must be marked as not being a wall. After this, we take
a look at the four neighbouring elements and remember which we have already
treated. Of all the neighbouring elements, we take one at random and
walk in that direction. Therefore, the wall element in that direction has
to be removed and then, we call the function recursively for that element.
The maze is only completed if we iterate the above procedure for
all neighbouring elements (in random order) and for our present
element by recursively calling the function for the present element. This
last iteration could have been done in a loop,
but it is done much simpler recursively.
Notice that elements with coordinates that are both odd are assumed to be
on our way through the maze and the generating process cannot terminate
as long as there is such an element not being delete
d. All other
elements are potentially part of the wall.
function MakeMaze(x, y) { delete Maze[x, y] # here we are, we have no wall here p = 0 # count unvisited fields in all directions if (x-2 SUBSEP y in Maze) d[p++] = "-x" if (x SUBSEP y-2 in Maze) d[p++] = "-y" if (x+2 SUBSEP y in Maze) d[p++] = "+x" if (x SUBSEP y+2 in Maze) d[p++] = "+y" if (p>0) { # if there are univisited fields, go there p = int(p*rand()) # choose one unvisited field at random if (d[p] == "-x") { delete Maze[x - 1, y]; MakeMaze(x - 2, y) } else if (d[p] == "-y") { delete Maze[x, y - 1]; MakeMaze(x, y - 2) } else if (d[p] == "+x") { delete Maze[x + 1, y]; MakeMaze(x + 2, y) } else if (d[p] == "+y") { delete Maze[x, y + 1]; MakeMaze(x, y + 2) } # we are back from recursion MakeMaze(x, y); # try again while there are unvisited fields } }