File: PQuilt.mesa   
Copyright © 1984 by Xerox Corporation. All rights reserved.
Created by: Bob Mayo, July 20, 1984 4:20:05 pm PDT
Last Edited by: Mayo, July 27, 1984 12:55:41 pm PDT
-- This program forms a stream oriented interface to a subset of the Parquet operations.
-- It can generate rectangular arrays.
PQuilt reads tokens (things separated by white space) from the input file and assigns this interpretation to them:
token = "$NEWROW" =>
Skip to a new row.
token = #abcd =>
Set the current rotation according to the string abcd. All subsequent tiles will be placed using this orientation. The string may contain any number of characters, and a transform is constructed starting from an identity transform by scanning the characters left to right. An 'X' causes a mirror in x (x coordinates are flipped), a 'Y' does the same in y. A '9' rotates clockwise by 90 degrees, a '1' by 180 degrees, and a '2' by 270 degrees. An '=' stands for the current orientation before this token was read. Examples: #=x mirrors the current transform in X, #x sets the transform to be a mirror in x, and #9x sets the transform to a 90 degree rotation followed by a mirror in x. Also, # alone just sets the transform back to the identity transform, while #= is a no-op.
token = anything else =>
Interpret the token as a tile name, and place that tile to the right of the last tile placed. If we just encounted a $NEWROW token instead place the tile below the first tile in the row above.
DIRECTORY
Parquet,
TerminalIO USING [RequestRope, WriteRope],
CDOrient USING [rotate90, rotate180, mirrorX, rotate270, ComposeOrient],
FS USING [StreamOpen, Error],
IO USING [STREAM, BreakProc, GetTokenRope, CharClass, EndOfStream, NUL, SP],
Rope USING [ROPE, Fetch, Equal, Length];
PQuilt: CEDAR PROGRAM    
IMPORTS Parquet, TerminalIO, CDOrient, IO, FS, Rope = BEGIN
PQuiltProc: Parquet.ModuleGeneratorProc = BEGIN
BreakProc: IO.BreakProc = BEGIN
IF char IN [IO.NUL .. IO.SP] THEN
RETURN[sepr]
ELSE
RETURN[other];
END;
-- user parameters
fileName, token, tileSetName: Rope.ROPE;
-- state information
tile: Parquet.Tile;
tiles: Parquet.Design;
module: Parquet.Module;
technology: Parquet.Technology;
prevTile: Parquet.PlacedTile;   -- the previous tile placed
prevRow: Parquet.PlacedTile;  -- the first tile in the previous row
newRow: BOOLTRUE;
orient: Parquet.Orientation ← Parquet.IdentityOrient;
-- constants
mirrorY: Parquet.Orientation ← CDOrient.ComposeOrient[CDOrient.ComposeOrient[CDOrient.rotate90, CDOrient.mirrorX], CDOrient.rotate270];
-- misc
stream: IO.STREAM;
endOfFile: BOOLFALSE;
-- query user for parameters
tileSetName ← TerminalIO.RequestRope["(PQuilt version 1) What file contains the tiles? "];
[tiles, technology, ] ← Parquet.ReadDesign[tileSetName];
IF tiles = NIL THEN BEGIN
TerminalIO.WriteRope["Could not load tiles.\n"];
GOTO Punt;
END;
fileName ← TerminalIO.RequestRope["What file contains the array description? "];
stream ← FS.StreamOpen[fileName ! FS.Error => IF error.group = user THEN {
TerminalIO.WriteRope["Could not open input file: '"];
TerminalIO.WriteRope[error.explanation];
TerminalIO.WriteRope["'.\n"];
GOTO Punt;
}];
-- initialize data structures
[module, prevTile] ← Parquet.NewModule[technology];
prevRow ← prevTile;
-- main body of module generator
DO
[token, ] ← IO.GetTokenRope[stream, BreakProc !IO.EndOfStream => {endOfFile ← TRUE; CONTINUE} ];
IF endOfFile THEN BEGIN
-- pass result back to Parquet
TerminalIO.WriteRope["PQuilt done.\n"];
RETURN[module];
END;
SELECT TRUE FROM
(Rope.Equal[token, "$NEWROW", FALSE]) => newRow ← TRUE;
(Rope.Fetch[token, 0] = '#) => BEGIN
newOrient: Parquet.Orientation ← Parquet.IdentityOrient;
FOR i: INT IN [1..Rope.Length[token]) DO
badMessage: BOOLFALSE;
SELECT Rope.Fetch[token, i] FROM
-- remember that Parquet's rotations are counter-clockwise
'= => newOrient ← CDOrient.ComposeOrient[newOrient, orient];
'1 => newOrient ← CDOrient.ComposeOrient[newOrient, CDOrient.rotate180];
'2 => newOrient ← CDOrient.ComposeOrient[newOrient, CDOrient.rotate90];
'9 => newOrient ← CDOrient.ComposeOrient[newOrient, CDOrient.rotate270];
'x, 'X => newOrient ← CDOrient.ComposeOrient[newOrient, CDOrient.mirrorX];
'y, 'Y => newOrient ← CDOrient.ComposeOrient[newOrient, mirrorY];
ENDCASE => BEGIN
IF ~badMessage THEN BEGIN
TerminalIO.WriteRope["Bad rotation specification: '"];
TerminalIO.WriteRope[token];
TerminalIO.WriteRope["' -- illegal characters ignored.\n"];
badMessage ← TRUE;
END;
END;
ENDLOOP;
orient ← newOrient;
END;
(TRUE) => BEGIN
tile ← Parquet.GetTile[tiles, token];
IF tile = NIL THEN BEGIN
TerminalIO.WriteRope["Could not find tile '"];
TerminalIO.WriteRope[token];
TerminalIO.WriteRope["'.\n"];
GOTO Punt;
END;
IF newRow THEN BEGIN
prevRow ← prevTile ← Parquet.AdjacentTile[prevRow, tile, below, orient];
newRow ← FALSE;
END
ELSE
prevTile ← Parquet.AdjacentTile[prevTile, tile, rightOf, orient];
END;
ENDCASE;
ENDLOOP;
EXITS
Punt => RETURN[NIL];
END;
-- register this generator with Parquet
[] ← Parquet.RegisterCommand["PQuilt", PQuiltProc];
END.