File: ModifyGerberFiles.mesa
Copyright Ó 1987 by Xerox Corporation. All rights reserved.
Christophe Cuenod February 15, 1988 3:47:55 pm PST
DIRECTORY
Commander USING [CommandProc, Register],
FS USING [StreamOpen],
GList USING [Reverse],
IO,
Rope USING [Equal, ROPE];
ModifyGerberFiles: CEDAR PROGRAM
IMPORTS Commander, FS, GList, IO, Rope =
BEGIN
ROPE: TYPE = Rope.ROPE;
Vector: TYPE = RECORD[
x: INT,
y: INT
];
FileInfoRep: TYPE = RECORD[
oldName: ROPE,
newName: ROPE,
min: Vector ← [32000, 32000],
max: Vector ← [0, 0],
midLine: Vector
];
FileInfo: TYPE = REF FileInfoRep;
FileList: TYPE = LIST OF FileInfo;
CommandsRep: TYPE = RECORD[
fileList: FileList,
newCommand: ARRAY[1..24] OF ROPE
];
Commands: TYPE = REF CommandsRep;
GetNumber: PROC [in: IO.STREAM] RETURNS [x: INT ← 0] ~ {
negatif: BOOLEANFALSE;
c: CHARIO.GetChar[in];
IF c = '- THEN {
negatif ← TRUE;
c ← IO.GetChar[in];
};
IF c = '+ THEN c ← IO.GetChar[in];
WHILE c IN ['0..'9] DO
x ← 10 * x + c - '0;
c ← IO.GetChar[in];
ENDLOOP;
IO.Backup[in, c];
IF negatif THEN x ← -x;
};
PutNumber: PROC [out: IO.STREAM, x: INT] ~ {
IO.PutF[out, "%g", IO.int[x]];
};
GerberToWheelNumber: PROC [d: CARD] RETURNS [number: CARD] ~ {
SELECT d FROM
IN[10..19] => number ← d-9; --This is Gerber standard definitions of apertures.
IN[70..71] => number ← d-59;
IN[20..29] => number ← d-7;
IN[72..73] => number ← d-49;
ENDCASE => ERROR;
};
WheelToGerberNumber: PROC [d: CARD] RETURNS [number: CARD] ~ {
SELECT d FROM
IN[1..10] => number ← d+9; 
IN[11..12] => number ← d+59;
IN[13..22] => number ← d+7;
IN[23..24] => number ← d+49;
ENDCASE => ERROR;
};
FindPosition: PROC [fileInfo: FileInfo] ~ {
oldPos, pos: Vector;
d: CARD;
c: CHAR;
end: BOOLEAN ← FALSE;
length, maxLength: CARD ← 0;
in: IO.STREAMFS.StreamOpen[fileInfo.oldName];
WHILE NOT IO.EndOf[in] DO
c ← IO.GetChar[in];
SELECT c FROM
='X  => {
pos.x ← GetNumber[in];
};
='Y  => {
pos.y ← GetNumber[in];
};
='D  => {
d ← GetNumber[in];
};
='M  => {
end ← TRUE;
};
='*  => {  -- Action takes place after a '*
SELECT d FROM
=0 => d ← 0 ; --Not yet initialised
=1 => { --Draw
fileInfo.min.x ← MIN[fileInfo.min.x, pos.x];
fileInfo.min.y ← MIN[fileInfo.min.y, pos.y];
fileInfo.max.x ← MAX[fileInfo.max.x, pos.x];
fileInfo.max.y ← MAX[fileInfo.max.y, pos.y];
length ← ABS[oldPos.x-pos.x]+ABS[oldPos.y-pos.y];
IF length > maxLength THEN { -- Takes the biggest line
maxLength ← length;
fileInfo.midLine.x ← (oldPos.x+pos.x)/2;
fileInfo.midLine.y ← (oldPos.y+pos.y)/2;
};
IF (length = maxLength)
AND (((oldPos.x+pos.x)/2 < fileInfo.midLine.x)
OR ((oldPos.y+pos.y)/2 < fileInfo.midLine.y))
THEN { -- Order the lines of equal length
fileInfo.midLine.x ← (oldPos.x+pos.x)/2;
fileInfo.midLine.y ← (oldPos.y+pos.y)/2;
};
oldPos ← pos;
};
=2 => { --Move
IF NOT end THEN { -- Moves to 0,0 at the end of the file !
fileInfo.min.x ← MIN[fileInfo.min.x, pos.x];
fileInfo.min.y ← MIN[fileInfo.min.y, pos.y];
fileInfo.max.x ← MAX[fileInfo.max.x, pos.x];
fileInfo.max.y ← MAX[fileInfo.max.y, pos.y];
};
oldPos ← pos;
};
=3 => { --Flash
fileInfo.min.x ← MIN[fileInfo.min.x, pos.x];
fileInfo.min.y ← MIN[fileInfo.min.y, pos.y];
fileInfo.max.x ← MAX[fileInfo.max.x, pos.x];
fileInfo.max.y ← MAX[fileInfo.max.y, pos.y];
};
ENDCASE;
};
ENDCASE;
ENDLOOP;
};
ModifyEachFile: PROC [fileInfo: FileInfo, commands: Commands, dx: CARD , dy: CARD] ~ {
pos, rel: Vector;
c: CHAR;
in2: IO.STREAM;
aperture: CARD;
d: INT ← 0;
in: IO.STREAMFS.StreamOpen[fileInfo.oldName];
out: IO.STREAMFS.StreamOpen[fileInfo.newName, $create];
WHILE NOT IO.EndOf[in] DO
c ← IO.GetChar[in];
SELECT c FROM
='G  => {
IO.PutChar[out, 'G];
PutNumber[out, GetNumber[in]];
};
='X  => {
pos.x ← GetNumber[in];
IO.PutChar[out, 'X];
PutNumber[out, pos.x-fileInfo.midLine.x+dx];
};
='Y  => {
pos.y ← GetNumber[in];
IO.PutChar[out, 'Y];
PutNumber[out, fileInfo.midLine.y-pos.y+dy];
};
='D  => {
d ← GetNumber[in];
SELECT d FROM
IN [1..2]  => {
IO.PutChar[out, c];
PutNumber[out, d]; --Draw or Move
};
=3  => {  --Flash
}; -- Takes realy place after a *
IN [4..99]  => {  --Select aperture
IO.PutChar[out, c];
PutNumber[out, d];
aperture ← GerberToWheelNumber[d];
};
ENDCASE => ERROR;
};
='\n  => IO.PutChar[out, c];
='*  => {
IF d # 3 THEN {
IO.PutChar[out, c]
}
ELSE {
IF commands.newCommand[aperture] = NIL THEN {
IO.PutF[out, "D3*"];
}
ELSE {
in2 ← IO.RIS[commands.newCommand[aperture]];
WHILE NOT IO.EndOf[in2] DO
c ← IO.GetChar[in2];
SELECT c FROM
='X  => {
rel.x ← GetNumber[in2];
IO.PutChar[out, 'X];
PutNumber[out, rel.x+pos.x-fileInfo.midLine.x+dx];
};
='Y  => {
rel.y ← GetNumber[in2];
IO.PutChar[out, 'Y];
PutNumber[out, rel.y+fileInfo.midLine.y-pos.y+dy];
};
='\040  => { -- Skips spaces
};
ENDCASE  => {
IO.PutChar[out, c];
};
ENDLOOP;
IO.PutF[out, "\nG54D%g*X%gY%g", IO.card[WheelToGerberNumber[aperture]], IO.card[pos.x-fileInfo.midLine.x+dx], IO.card[fileInfo.midLine.y-pos.y+dy]];
};
};
};
ENDCASE;
ENDLOOP;
IO.Close[out];
};
ReadCommands: PROC [commandFile: ROPE] RETURNS [commands: Commands] ~ {
r: ROPE;
fileInfo: FileInfo;
oldAperture: NAT;
in: IO.STREAMFS.StreamOpen[commandFile];
commands ← NEW[CommandsRep];
FOR i: NAT IN [1..24] DO
commands.newCommand[i] ← NIL;
ENDLOOP;
WHILE NOT IO.EndOf[in] DO
r ← NIL;
r ← IO.GetTokenRope[in! IO.EndOfStream => CONTINUE].token;
SELECT TRUE FROM
Rope.Equal[r, "ApertureModification", FALSE]  => {
oldAperture ← IO.GetCard[in];
commands.newCommand[oldAperture] ← IO.GetLineRope[in];
};
Rope.Equal[r, "File", FALSE]  => {
fileInfo ← NEW[FileInfoRep];
fileInfo.oldName ← IO.GetTokenRope[in].token;
fileInfo.newName ← IO.GetTokenRope[in].token;
commands.fileList ← CONS[fileInfo, commands.fileList];
};
r = NIL => r ← NIL;
ENDCASE => ERROR;
ENDLOOP;
};
ModifyGerberFilesProc: Commander.CommandProc = BEGIN
min, max: Vector ← [0,0];
commandFile: ROPEIO.GetTokenRope[IO.RIS[cmd.commandLine]].token;
commands: Commands ← ReadCommands[commandFile];
commands.fileList ← NARROW[GList.Reverse[commands.fileList]];
IO.PutF[cmd.out, "Reading the source files to find how to justify them\n"];
FOR l: FileList ← commands.fileList, l.rest UNTIL l = NIL DO
IO.PutF[cmd.out, "Reading %g \n", IO.rope[l.first.oldName]];
FindPosition[l.first];
ENDLOOP;
Taking the center of the longest line as reference what is the smallest rectangle fitting all the drawings
FOR l: FileList ← commands.fileList, l.rest UNTIL l = NIL DO
min.x ← MIN[min.x, l.first.min.x - l.first.midLine.x];
min.y ← MIN[min.y, l.first.min.y - l.first.midLine.y];
max.x ← MAX[max.x, l.first.max.x - l.first.midLine.x];
max.y ← MAX[max.y, l.first.max.y - l.first.midLine.y];
ENDLOOP;
The new coordinates are:
newx ← oldx - midline.x - min.x + epsilon
newy ← midline.y - oldy + max.y + epsilon
IO.PutF[cmd.out, "Generating the new files\n"];
FOR l: FileList ← commands.fileList, l.rest UNTIL l = NIL DO
IO.PutF[cmd.out, "Reading %g and writing %g\n", IO.rope[l.first.oldName], IO.rope[l.first.newName]];
ModifyEachFile[l.first, commands, -min.x+100, max.y+100];
ENDLOOP;
END;
Commander.Register[
key: "ModifyGerberFiles", proc: ModifyGerberFilesProc, doc: "Transform a set of gerber files to prepare them for photoploting (Invert the Y axis, justify the drawings and change some aperture shooting by an arbitrary pattern).\n"];
END.