DIRECTORY
Commander USING [CommandProc, Register, Handle],
Convert USING [CardFromRope, RealFromRope],
FS USING [Error, FileInfo, StreamOpen],
IO,
Rope USING [Cat, Equal, ROPE],
Imager USING [Color, Context, MaskVector, SetColor, SetStrokeEnd, SetStrokeWidth, StrokeEnd, VEC, white, black, MakeGray],
ImagerColor USING[ColorFromRGB],
ImagerInterpress USING [Close, Create, DoPage, Ref];
GerberToIP:
CEDAR
PROGRAM
IMPORTS Commander, Convert, FS, Imager, ImagerColor, ImagerInterpress, IO, Rope =
BEGIN
ROPE: TYPE = Rope.ROPE;
FileHandleRep: TYPE = RECORD[
ip: ROPE,
parameters: ROPE,
gerber: ROPE
];
FileHandle: TYPE = REF FileHandleRep;
Shape: TYPE = {standard, donut, thermalRelief, target};
Aperture:
TYPE =
RECORD[
dx1, dx2: REAL ← 0,
dy1, dy2: REAL ← 0,
shape: Shape,
color: Imager.Color,
strokeEnd: Imager.StrokeEnd
];
Parameters:
TYPE =
RECORD[
invertAxis: BOOLEAN,
scale: REAL,
xSym: REAL,
ySym: REAL,
xTransl: REAL,
yTransl: REAL,
aperture: ARRAY[1..24] OF Aperture
];
BadCommandLine: SIGNAL [];
CmdTokenBreak:
PROC [char:
CHAR]
RETURNS [
IO.CharClass] = {
IF char = '← THEN RETURN [break];
IF char = ' OR char = '\t OR char = ', OR char = '; OR char = '\n THEN RETURN [sepr];
RETURN [other];
};
GetCmdToken:
PROC [stream:
IO.
STREAM]
RETURNS [rope:
ROPE] = {
rope ← NIL;
rope ← stream.GetTokenRope[CmdTokenBreak ! IO.EndOfStream => CONTINUE].token;
};
ReadCommandLine:
PROC [cmd: Commander.Handle]
RETURNS [filehandle: FileHandle] ~ {
file: ROPE;
stream: IO.STREAM;
filehandle ← NEW[FileHandleRep];
stream ← IO.RIS[cmd.commandLine];
file ← GetCmdToken[stream];
filehandle.ip ← Rope.Cat[file, ".ip"];
filehandle.gerber ← Rope.Cat[file, ".gerber"];
filehandle.parameters ← Rope.Cat[file, ".parameters"];
[] ←
FS.FileInfo[filehandle.gerber !
FS.Error => {
IF error.group = user
THEN {
cmd.out.PutRope[Rope.Cat[error.explanation, "\n"]];
SIGNAL BadCommandLine[];
CONTINUE;
};
}];
[] ←
FS.FileInfo[filehandle.parameters !
FS.Error => {
IF error.group = user
THEN {
cmd.out.PutRope[Rope.Cat[error.explanation, "\n"]];
SIGNAL BadCommandLine[];
CONTINUE;
};
}];
IF file =
NIL
THEN {
cmd.out.PutRope["Usage:\nGerberToIP Filename\n(Transform the file Filename.gerber, into the file Filename.ip )\n"];
SIGNAL BadCommandLine[];
};
};
GetNumber:
PROC [in:
IO.
STREAM]
RETURNS [x:
CARD ← 0] ~ {
c: CHAR ← IO.GetChar[in];
WHILE c
IN ['0..'9]
DO
x ← 10 * x + c - '0;
c ← IO.GetChar[in];
ENDLOOP;
IO.Backup[in, c];
};
SetAperture:
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;
};
TokenProc:
IO.BreakProc = {
RETURN [
SELECT char
FROM
'[, '], '(, '), '{, '}, '", '*, '/, '@, '← => break,
IN [IO.NUL .. IO.SP] => sepr,
',, ':, '; => sepr,
ENDCASE => other];
};
ReadParameters:
PROC [parametersFile:
ROPE]
RETURNS [parameters: Parameters] ~ {
r, s: ROPE;
index: CARD;
in: IO.STREAM ← FS.StreamOpen[parametersFile];
parameters.invertAxis ← FALSE;
parameters.scale ← 1.0;
parameters.xSym ← 1.0;
parameters.ySym ← 1.0;
parameters.xTransl ← 0.0;
parameters.yTransl ← 0.0;
WHILE
NOT
IO.EndOf[in]
DO
r ← NIL;
r ← IO.GetTokenRope[in! IO.EndOfStream => CONTINUE].token;
SELECT
TRUE
FROM
Rope.Equal[r, "invertaxis",
FALSE] => {
parameters.invertAxis ← TRUE;
};
Rope.Equal[r, "scale",
FALSE] => {
parameters.scale ← Convert.RealFromRope[IO.GetTokenRope[in].token];
};
Rope.Equal[r, "xSymetry",
FALSE] => {
parameters.xSym ← -1.0;
};
Rope.Equal[r, "ySymetry",
FALSE] => {
parameters.ySym ← -1.0;
};
Rope.Equal[r, "xTranslation",
FALSE] => {
parameters.xTransl ← Convert.RealFromRope[IO.GetTokenRope[in, TokenProc].token];
};
Rope.Equal[r, "yTranslation",
FALSE] => {
parameters.yTransl ← Convert.RealFromRope[IO.GetTokenRope[in, TokenProc].token];
};
Rope.Equal[r, "aperture",
FALSE] => {
index ← Convert.CardFromRope[IO.GetTokenRope[in].token];
s ← IO.GetTokenRope[in].token;
SELECT
TRUE
FROM
Rope.Equal[s, "standard",
FALSE] => {
parameters.aperture[index].shape ← standard;
};
Rope.Equal[s, "donut",
FALSE] => {
parameters.aperture[index].shape ← donut;
};
Rope.Equal[s, "thermalRelief",
FALSE] => {
parameters.aperture[index].shape ← thermalRelief;
};
Rope.Equal[s, "target",
FALSE] => {
parameters.aperture[index].shape ← target;
};
ENDCASE => ERROR;
s ← IO.GetTokenRope[in].token;
SELECT
TRUE
FROM
Rope.Equal[s, "round",
FALSE] => {
parameters.aperture[index].strokeEnd ← round;
parameters.aperture[index].dx1 ← Convert.CardFromRope[IO.GetTokenRope[in].token]/2;
parameters.aperture[index].dx2 ← parameters.aperture[index].dx1;
parameters.aperture[index].dy1 ← parameters.aperture[index].dx1;
parameters.aperture[index].dy2 ← parameters.aperture[index].dx1;
};
Rope.Equal[s, "square",
FALSE] => {
parameters.aperture[index].strokeEnd ← square;
parameters.aperture[index].dx1 ← Convert.CardFromRope[IO.GetTokenRope[in].token]/2;
parameters.aperture[index].dx2 ← parameters.aperture[index].dx1;
parameters.aperture[index].dy1 ← parameters.aperture[index].dx1;
parameters.aperture[index].dy2 ← parameters.aperture[index].dx1;
};
Rope.Equal[s, "rectangle",
FALSE] => {
parameters.aperture[index].strokeEnd ← square;
parameters.aperture[index].dx1 ← Convert.CardFromRope[IO.GetTokenRope[in].token];
parameters.aperture[index].dx2 ← Convert.CardFromRope[IO.GetTokenRope[in].token];
parameters.aperture[index].dy1 ← Convert.CardFromRope[IO.GetTokenRope[in].token];
parameters.aperture[index].dy2 ← Convert.CardFromRope[IO.GetTokenRope[in].token];
};
ENDCASE => ERROR;
s ← IO.GetTokenRope[in].token;
SELECT
TRUE
FROM
Rope.Equal[s, "White",
FALSE] => {
parameters.aperture[index].color ← Imager.white;
};
Rope.Equal[s, "Gray",
FALSE] => {
parameters.aperture[index].color ← Imager.MakeGray[0.5];
};
Rope.Equal[s, "Black",
FALSE] => {
parameters.aperture[index].color ← Imager.black;
};
Rope.Equal[s, "Red",
FALSE] => {
parameters.aperture[index].color ← ImagerColor.ColorFromRGB[[1, 0, 0]];
};
Rope.Equal[s, "Green",
FALSE] => {
parameters.aperture[index].color ← ImagerColor.ColorFromRGB[[0, 1, 0]];
};
Rope.Equal[s, "Blue",
FALSE] => {
parameters.aperture[index].color ← ImagerColor.ColorFromRGB[[0, 0, 1]];
};
Rope.Equal[s, "Cyan",
FALSE] => {
parameters.aperture[index].color ← ImagerColor.ColorFromRGB[[0, 1, 1]];
};
Rope.Equal[s, "Magenta",
FALSE] => {
parameters.aperture[index].color ← ImagerColor.ColorFromRGB[[1, 0, 1]];
};
Rope.Equal[s, "Yellow",
FALSE] => {
parameters.aperture[index].color ← ImagerColor.ColorFromRGB[[1, 1, 0]];
};
ENDCASE => ERROR;
};
ENDCASE;
ENDLOOP;
};
ProduceIPMaster:
PROC [cmd: Commander.Handle, fileHandle: FileHandle] ~ {
DoPrint:
PROC [context: Imager.Context] ~ {
SetParam:
PROC [] ~ {
IF param.aperture[index].color =
NIL
THEN {
IO.PutF[cmd.out, "The aperture %g appears in the %g file and is not defined in the %g file.\n", IO.card[index], IO.rope[fileHandle.gerber], IO.rope[fileHandle.parameters]];
param.aperture[index].color ← Imager.white;
}
ELSE {
IF currentColor # param.aperture[index].color
THEN {
currentColor ← param.aperture[index].color;
Imager.SetColor[context, currentColor];
};
IF currentStrokeEnd # param.aperture[index].strokeEnd
THEN {
currentStrokeEnd ← param.aperture[index].strokeEnd;
Imager.SetStrokeEnd[context, currentStrokeEnd];
};
widthx ← param.aperture[index].dx1+param.aperture[index].dx2;
widthy ← param.aperture[index].dy1+param.aperture[index].dy2;
width ← MIN[widthx, widthy];
IF widthx # widthy
THEN {
delta1.x ← param.aperture[index].dx1-width/2;
delta2.x ← param.aperture[index].dx2-width/2;
delta1.y ← param.aperture[index].dy1-width/2;
delta2.y ← param.aperture[index].dy2-width/2;
IF param.invertAxis
THEN {
temp ← delta1.x;
delta1.x ← delta1.y;
delta1.y ← temp;
temp ← delta2.x;
delta2.x ← delta2.y;
delta2.y ← temp;
}
}
ELSE {
delta1.x ← width/1000;
delta2.x ← 0;
delta1.y ← 0;
delta2.y ← 0;
};
IF currentStrokeWidth #
MIN[widthx, widthy]
THEN {
currentStrokeWidth ← MIN[widthx, widthy];
Imager.SetStrokeWidth[context, currentStrokeWidth];
};
};
};
currentStrokeEnd: Imager.StrokeEnd;
currentStrokeWidth: REAL;
currentColor: Imager.Color;
index: CARD;
c: CHAR;
d: INT;
widthx, widthy, width, temp: REAL;
p1, p2: Imager.VEC;
oldPos, pos, delta1, delta2: Imager.VEC;
in: IO.STREAM ← FS.StreamOpen[fileHandle.gerber];
d ← 0;
WHILE
NOT
IO.EndOf[in]
DO
c ← IO.GetChar[in];
SELECT c
FROM
='X => {
IF
NOT param.invertAxis
THEN {
pos.x ←param.xTransl+param.xSym*GetNumber[in];
}
ELSE {
pos.y ←param.yTransl+param.ySym*GetNumber[in];
};
};
='Y => {
IF
NOT param.invertAxis
THEN {
pos.y ←param.yTransl+param.ySym*GetNumber[in];
}
ELSE {
pos.x ←param.xTransl+param.xSym*GetNumber[in];
};
};
='D => d ← GetNumber[in];
='* => {
-- Action takes place after a '*
SELECT d
FROM
=0 => d ← 0 ; --Not yet initialised
=1 => {
--Draw
SetParam[];
Imager.MaskVector[context, oldPos, pos];
oldPos ← pos;
};
=2 => {
--Move
oldPos ← pos;
};
=3 => {
--Flash
SetParam[];
p1.x ← pos.x-delta1.x;
p1.y ← pos.y-delta1.y;
p2.x ← pos.x+delta2.x;
p2.y ← pos.y+delta2.y;
Imager.MaskVector[context, p1, p2];
oldPos ← pos;
IF param.aperture[index].shape = target
THEN {
--add 2 white offset squares
currentStrokeEnd ← square;
Imager.SetStrokeEnd[context, currentStrokeEnd];
currentColor ← Imager.white;
Imager.SetColor[context, currentColor];
p1.x ← pos.x-currentStrokeWidth*.5;
p1.y ← pos.y-currentStrokeWidth*.5;
p2.x ← pos.x-currentStrokeWidth*.6;
p2.y ← pos.y-currentStrokeWidth*.5;
Imager.MaskVector[context, p1, p2];
p1.x ← pos.x+currentStrokeWidth*.5;
p1.y ← pos.y+currentStrokeWidth*.5;
p2.x ← pos.x+currentStrokeWidth*.6;
p2.y ← pos.y+currentStrokeWidth*.5;
Imager.MaskVector[context, p1, p2];
};
IF param.aperture[index].shape = donut OR param.aperture[index].shape = thermalRelief
THEN {
--add a white center
currentStrokeWidth ← currentStrokeWidth*.6;
Imager.SetStrokeWidth[context, currentStrokeWidth];
currentColor ← Imager.white;
Imager.SetColor[context, currentColor];
Imager.MaskVector[context, p1, p2];
};
IF param.aperture[index].shape = thermalRelief
THEN {
--add a white cross
currentStrokeEnd ← square;
Imager.SetStrokeEnd[context, currentStrokeEnd];
currentStrokeWidth ← currentStrokeWidth/3;
Imager.SetStrokeWidth[context, currentStrokeWidth];
p1.x ← pos.x-currentStrokeWidth*2.1;
p1.y ← pos.y;
p2.x ← pos.x+currentStrokeWidth*2.1;
p2.y ← pos.y;
Imager.MaskVector[context, p1, p2];
p1.x ← pos.x;
p1.y ← pos.y-currentStrokeWidth*2.1;
p2.x ← pos.x;
p2.y ← pos.y+currentStrokeWidth*2.1;
Imager.MaskVector[context, p1, p2];
};
};
IN[4..99] => index ← SetAperture[d];
ENDCASE => ERROR;
};
ENDCASE;
ENDLOOP;
};
pressScale: REAL;
param: Parameters ← ReadParameters[fileHandle.parameters];
ip: ImagerInterpress.Ref ← ImagerInterpress.Create[fileHandle.ip];
pressScale ← param.scale*0.0000256;
ImagerInterpress.DoPage[ip, DoPrint, pressScale];
ImagerInterpress.Close[ip];
};
GerberToIPProc: Commander.CommandProc =
BEGIN
fault: BOOLEAN ← FALSE;
fileHandle: FileHandle;
fileHandle ← ReadCommandLine[cmd ! BadCommandLine => {
fault ← TRUE;
CONTINUE;
}];
IF fault THEN RETURN;
ProduceIPMaster[cmd, fileHandle];
END;
Commander.Register[
key: "GerberToIP", proc: GerberToIPProc, doc: "Transform a gerber file into an interpress file.\n"];