BrandyCIFter.mesa A module to output CIF files from Chipndale
Copyright © 1984 by Xerox Corporation. All rights reserved.
Last Edited by: Kimr, October 15, 1984 5:15:13 pm PDT
Last Edited by: Jacobi, October 30, 1984 12:10:38 pm PST
Last Edited by McCreight, October 17, 1984 7:02:18 pm PDT
DIRECTORY
TerminalIO USING [RequestRope, RequestInt, WriteRope, WriteInt],
CDOrient USING [MapRect, original, mirrorX, rotate90, rotate180, rotate270, ConcentrateOnRotate90, IncludesMirrorX, ComposeOrient],
CD USING [Level, LevelKey, Rect, FetchLevel, lambda, ObPtr, CellPtr, Number, Position, DesignNumber, DesignPosition, DesignRect, Application, ApplicationPtr, ApplicationList, CellRecord, Orientation, ObjectDefinition, Properties, Design, NewNullDeviceDrawRef, DrawRectProc, DrawRef, DrawProc, DrawInformation],
CDMenus,
CornerStitching USING [ Tesselation, TilePtr, NewTesselation, ContentsBound, ChangeRect, EnumerateArea, Area, Value],
IO USING [ STREAM, time, PutRope, PutChar, CR, PutF, PutFR, int, rope, refAny, Close],
List USING [Append],
FS USING [StreamOpen, Error],
Rope USING [ROPE, Concat, IsEmpty, FromRefText],
Atom USING [GetPropFromList, PutPropOnList, GetPName],
CDApplications USING [ARectO],
CDBasics USING [ empty, universe, Extend, NormalizeRect, NonEmpty, Intersection, Surround, Center],
CDOps USING [DrawDesign],
CDProperties USING [GetPropFromLevel, PutPropOnObject],
CDSequencer USING [Command, ImplementCommand],
CDViewer USING [ShowArrow, RemoveArrow];
BrandyCIFter:
CEDAR
PROGRAM
IMPORTS
List, CornerStitching, TerminalIO, CDMenus, CDOrient, CD, CDBasics, IO, FS, Rope, Atom, CDApplications, CDOps, CDProperties, CDSequencer, CDViewer =
BEGIN
TYPES
CIFUnits: TYPE = CD.Number;
CIFRect: TYPE = CD.Rect -- in CIFUnits -- ;
Rational: TYPE = RECORD [num, denom: INT ← 1];
CoordRect: TYPE = RECORD [ x1, y1, x2, y2: CD.DesignNumber ];
CIFDest: TYPE = REF CIFDestRec ← NIL;
CIFDestRec:
TYPE =
RECORD [
cifDest: Rope.ROPE,
deltaRadius: INT -- nanometers, + means cif is bigger
];
CIFDirective: TYPE = REF CIFDirectiveRec ← NIL;
CIFDirectiveRec:
TYPE =
RECORD [
cdSource: CD.Level,
cifDest: Rope.ROPE,
deltaRadius: INT -- nanometers, + means cif is bigger
];
MaskStateRef: TYPE = REF MaskState;
MaskState:
TYPE =
RECORD [
areas, unbloated: REF CornerStitching.Tesselation ← NIL,
partClip: CIFRect ← [0,0,0,0]
];
Global Variables
debugging: BOOLEAN ← TRUE; -- used for printing out debugging messages
design: CD.Design ; -- Chipndale data structure for which CIF will be written
mainOb: CD.ObPtr ← NEW[CD.ObjectDefinition];
topLevelOb: CD.ObPtr ← NEW[CD.ObjectDefinition];
mainCellPtr: CD.CellPtr ← NEW[CD.CellRecord];
stopFlag: REF BOOLEAN ← NEW[BOOLEAN];
infinity: CD.DesignNumber ← LAST[CD.DesignNumber]; --used to initialize sizes and positions
cellCount: INTEGER ← 0; -- used to assign consecutive CIF ID numbers
cifLayerAnnounced:
BOOLEAN ←
FALSE;
level assumed to be unchanged until the next level is announced
lambda: INT ← CD.lambda;
cifPerLambda: INT ← 100; -- CIF units per lambda
nmPerCIF: INT = 10; -- nanometers per CIF unit, given 0.01 microns per CIF unit
cifPerCD: INT; -- cifPerLambda/l, since l is defined in CD units per lambda
cifScaling: Rational;
--..output numbers*cifScaling = CIFUnits
--..CIFUnits/cifScaling = output numbers
cifFile: IO.STREAM;-- file where output will be stored
comment, base, name, lastCellName: Rope.ROPE;
mainRect: CoordRect ← [x1: infinity, y1: infinity, x2:-infinity, y2:-infinity];
--set to design size in cifMeasureArea, used to switch between Chipndale and CIF coordinate systems
cifDirectives: LIST OF REF ANY -- CIFDirective -- ← NIL;
currentDirective: CIFDirective;
partWidth: CIFUnits ← 800000; -- window for flat CIF
partHeight: CIFUnits ← 800000;
Procedures
clearIDsRec: CD.DrawRef ← NEW[CD.DrawInformation];
ClearCellIDs:
PROC [ aptr:
CD.ApplicationPtr, pos:
CD.DesignPosition, orient:
CD.Orientation, pr:
REF
CD.DrawInformation ]
-- CD.DrawProc -- =
--used to set all CellIDNo's to NIL at the start
BEGIN
zeroID: REF INTEGER ← NIL;
CDProperties.PutPropOnObject[onto: aptr.ob, prop: $CellIDNo, val: zeroID];
IF aptr.ob.p.inDirectory
THEN
aptr.ob.p.drawMe[aptr: aptr, pos: aptr.location, orient: aptr.orientation, pr: clearIDsRec];
END;
ValueOf:
PROC[ prop:
CD.Properties, key:
ATOM]
RETURNS [
REF
ANY] =
Returns valueptr given key from object's property field
{ RETURN [Atom.GetPropFromList[prop, key]]};
SymHeader:
PROC [obj:
CD.ObPtr]=
start outputting a new cell and assign its cellID
BEGIN
cellID: REF INT ← NEW[INT ← (cellCount ← cellCount + 1)];
cifFile.PutF["DS %d %d %d;\n",
IO.int[cellCount], IO.int[cifScaling.num], IO.int[cifScaling.denom]];
obj.properties ← Atom.PutPropOnList[ obj.properties, $CellIDNo, cellID];
END;
SymTrailer:
PROC =
finish off cell description
BEGIN
cifFile.PutRope["DF;\n"];
END;
GenerateGeometricOb:
PROC [obj:
CD.ObPtr, ap:
CD.ApplicationPtr, dr:
CD.DrawRef] =
output CIF for structures only identified as geometries
BEGIN
SymHeader[obj];
FOR directives:
LIST
OF
REF
ANY ← cifDirectives, directives.rest
WHILE directives#
NIL
DO
BEGIN
currentDirective ← NARROW[directives.first];
cifLayerAnnounced ← FALSE;
IF currentDirective.deltaRadius>=0
THEN
ap.ob.p.drawMe[aptr: ap, pos: [x: 0, y: 0], orient: 0, pr: dr];
END;
ENDLOOP;
SymTrailer[];
END; -- of GenerateGeometricOb
CIFDefineObject:
PROC [obj:
CD.ObPtr] =
Print out CIF pertaining to object starting with the furthest object from the top; After leaving procedure, object will have been assigned a CIF ID # in its properties field
BEGIN
ap: CD.ApplicationPtr;-- used as an abbrev. for "aplist.first"
dummyApPtr: CD.ApplicationPtr ← NEW[CD.Application]; -- used to match the type a drawMe parameter requires
value: Rope.ROPE;-- used to determine signal names
IF debugging THEN TerminalIO.WriteRope["Entering CIFDefineObject\n"];
IF ValueOf[obj.properties, $CellIDNo] #
NIL
THEN
RETURN;
non-NIL value indicates that object has already been assigned an ID # and is therefore already defined
We should also make a test here to decide whether any hierarchical geometry results from this object, and assign a special value to this object's attribute $CellIDNo if not.
WITH obj.specificRef SELECT FROM
cellptr:
CD.CellPtr =>
BEGIN
aplist: CD.ApplicationList;
trace each member of the list to the bottom before any output is made
FOR aplist ← cellptr.contents, aplist.rest
WHILE aplist #
NIL
DO
CIFDefineObject[aplist.first.ob]
ENDLOOP;
SymHeader[obj];
--write out the name of the cell
CIFDrawCellName[cellptr.name];
For each member of the application list, determine whether obj is a cell. If so, determine transformations
FOR aplist ← cellptr.contents, aplist.rest
WHILE aplist #
NIL
DO
SELECT aplist.first.ob.specificRef
FROM
NIL => NULL;-- NIL indicates Rect
ENDCASE =>
BEGIN
cifRect: CIFRect;
IF debugging THEN TerminalIO.WriteRope[" Transformations\n"];
ap ← aplist.first;
cifRect ← CDRectToCIF[CDOrient.MapRect[
itemInCell: [0, 0, 0, 0],
cellSize: [x: ap.ob.size.x, y: ap.ob.size.y],
cellInstOrient: ap.orientation,
cellInstPos: [x: ap.location.x, y: ap.location.y]]];
CIFSymbolCall[ap.ob, ap.orientation, [cifRect.x1, cifRect.y1]];
END;
ENDLOOP;
for each level, pick out the rectangles belonging to that level
and output them
FOR directives:
LIST
OF
REF
ANY ← cifDirectives, directives.rest
WHILE directives#
NIL
DO
BEGIN
currentDirective ← NARROW[directives.first];
cifLayerAnnounced ← FALSE;
FOR aplist ← cellptr.contents, aplist.rest
WHILE aplist #
NIL
DO
BEGIN
ap ← aplist.first;
SELECT ap.ob.specificRef
FROM
NIL =>
-- indicates Rect
IF currentDirective.deltaRadius>=0
THEN
ap.ob.p.drawMe[ aptr: ap, pos: ap.location, orient: ap.orientation, pr: cifDrawRec];
ENDCASE => NULL;
END;
ENDLOOP;
END;
ENDLOOP;
--generate all signal names
FOR aplist ← cellptr.contents, aplist.rest
WHILE aplist #
NIL
DO
BEGIN
value ←NARROW[
Atom.GetPropFromList[aplist.first.ob.properties,$signalName]];
IF ValueOf[aplist.first.ob.properties, $signalName] #
NIL
THEN
SELECT aplist.first.ob.specificRef
FROM
NIL => CIFLabelTerminalIO[aplist.first, value, aplist.first.ob.level];
ENDCASE => NULL;
END;
ENDLOOP;
spit out properties other than signal names as comments
spit out complaints about shorting contact
SymTrailer[];
END; -- of cell
ENDCASE =>
-- transistors or geometry or rects
IF obj.specificRef = NIL THEN NULL
ELSE
BEGIN
dummyApPtr^.ob ← obj; -- drawMe requires an ApplicationPtr
GenerateGeometricOb[obj: obj, ap: dummyApPtr , dr: cifDrawRec];
END;
IF debugging THEN TerminalIO.WriteRope["Leaving CIFDefineObject\n"];
END; -- of CIFDefineObject
CIFSymbolCall:
PROC[ob:
CD.ObPtr, orient:
CD.Orientation ← CDOrient.original, pos:
CD.Position
-- CIFUnits -- ← [0,0]] =
indicate which cell is called using its ID number
BEGIN
v: REF = ValueOf[ob.properties, $CellIDNo];
IF v = NIL THEN ERROR;
WITH v
SELECT
FROM
IDNo:
REF
INT =>
BEGIN
cifFile.PutF["C %d", IO.int[IDNo^]];
IF orient # 0
THEN
BEGIN
xrotation: CD.Orientation ← CDOrient.ConcentrateOnRotate90[orient];
IF xrotation # 0
THEN
BEGIN
cifFile.PutRope[" R "];
cifFile.PutRope[
SELECT xrotation
FROM
CDOrient.rotate90 => "0,1" ,
CDOrient.rotate180=> "-1,0",
CDOrient.rotate270=> "0,-1",
ENDCASE=> "1,0"]; -- never use this one
END;
IF CDOrient.IncludesMirrorX[orient]
THEN
cifFile.PutRope[" M X "];
END;
IF (pos.x # 0)
OR (pos.y # 0)
THEN
BEGIN
cifFile.PutRope[" T "];
CIFOutPoint[pos.x, pos.y];
END;
cifFile.PutRope[";\n"];
END;
ENDCASE => NULL;
END;
OUTPUT PROCEDURES
cifMeasureR:
CD.DrawRef ←
CD.NewNullDeviceDrawRef[design: design];
used as a parameter to DrawDesign in order to call cifMeasureArea, fields initialized later
cifMeasureArea:
PROC [ r:
CD.DesignRect, l:
CD.Level, pr:
CD.DrawRef]
-- CD.DrawRectProc -- =
Sets mainRect to be a large as is necessary to contain design.
BEGIN
r ← CDBasics.NormalizeRect[r];
IF NOT CDBasics.NonEmpty[r] THEN RETURN;
mainRect ←
[x1: MIN[r.x1, mainRect.x1], y1: MIN[r.y1, mainRect.y1],
x2: MAX[r.x2, mainRect.x2], y2: MAX[r.y2, mainRect.y2]];
END; -- of cifMeasureArea
cifDrawRec:
CD.DrawRef ←
CD.NewNullDeviceDrawRef[design: design];
used as a parameter to drawMe in order to call cifOrArea, fields intitialized later
cifOrArea:
PROC [r:
CD.DesignRect, l:
CD.Level, pr:
CD.DrawRef] =
BEGIN
rd: CD.DesignRect;
IF (l # currentDirective.cdSource) OR NOT CDBasics.NonEmpty[r] THEN RETURN;
rd ← CDBasics.NormalizeRect[r];
CIFRectOut[CDBasics.Extend[r: CDRectToCIF[CDBasics.NormalizeRect[r]], ext: currentDirective.deltaRadius/nmPerCIF]];
END;
CIFRectOut:
PROC [r: CIFRect] =
BEGIN
IF NOT CDBasics.NonEmpty[r] THEN RETURN;
IF
NOT cifLayerAnnounced
THEN
BEGIN
cifFile.PutF["L %g;\n", IO.rope[currentDirective.cifDest]];
cifLayerAnnounced ← TRUE;
END;
cifFile.PutF["B %d %d %d %d;\n",
-- BOX command, in prescaled CIF units
IO.int[PrescaleCIF[[r.x2-r.x1, 1]]] -- length -- ,
IO.int[PrescaleCIF[[r.y2-r.y1, 1]]] -- width -- ,
IO.int[PrescaleCIF[[r.x1+r.x2, 2]]] -- center x-coord -- ,
IO.int[PrescaleCIF[[r.y1+r.y2, 2]]] -- center y-coord -- ];
END;
CIFOutPoint:
PROC[x, y: CIFUnits] =
{cifFile.PutF["%d,%d", IO.int[PrescaleCIF[[x,1]]], IO.int[PrescaleCIF[[y,1]]]]};
CIFLabelTerminalIO:
PROC [ ap:
CD.ApplicationPtr, s: Rope.
ROPE, lev:
CD.Level] =
BEGIN
pt: CD.Position ← CDBasics.Center[CDRectToCIF[CDApplications.ARectO[ap]]];
cifFile.PutF["94 %s ", IO.rope[s]];
CIFOutPoint[pt.x, pt.y];
cifFile.PutChar[' ];
FOR directives:
LIST
OF
REF
ANY ← cifDirectives, directives.rest
WHILE directives#
NIL
DO
d: CIFDirective = NARROW[directives.first];
IF d.cdSource = lev THEN {cifFile.PutRope[d.cifDest]; EXIT};
ENDLOOP;
cifFile.PutRope[";\n"];
END;
CIFDrawCellName:
PROC[s: Rope.
ROPE] =
BEGIN
lastCellName ← s;
cifFile.PutF["9 %s;\n", IO.rope[IF Rope.IsEmpty[s] THEN "unnamed cell" ELSE s]];
END;
RopeNeeded: SIGNAL [ ref: REF REF ] = CODE;
ToRope:
PROC [ ref:
REF ]
RETURNS [ rope: Rope.
ROPE ] =
BEGIN
IF ref = NIL THEN rope ← NIL
ELSE
WITH ref
SELECT
FROM
r: Rope.ROPE => rope ← r;
rt: REF TEXT => rope ← Rope.FromRefText[rt];
a: ATOM => rope ← Atom.GetPName[a];
ri: REF INT => rope ← IO.PutFR[format: "%d", v1: IO.int[ri^]];
ENDCASE =>
BEGIN
refRef: REF REF = NEW[REF ← ref];
SIGNAL RopeNeeded[refRef];
rope ← ToRope[refRef^ ! RopeNeeded => GOTO NoHelp];
END;
END;
CantRepresentExactly: ERROR = CODE;
SetCIFScaling:
PROC =
BEGIN
cifScaling ← ReduceTerms[[num: cifPerLambda, denom: 2*lambda]]; -- the "2" allows for box centers
FOR directives:
LIST
OF
REF
ANY ← cifDirectives, directives.rest
WHILE directives#
NIL
DO
d: CIFDirective = NARROW[directives.first];
cif: CIFUnits;
IF
ABS[d.deltaRadius]
MOD nmPerCIF # 0
THEN
ERROR CantRepresentExactly;
cif ← ABS[d.deltaRadius]/nmPerCIF;
cifScaling ← ReduceTerms[[
num: cifScaling.num,
denom: (cifScaling.num*cifScaling.denom)/GCD[cif*cifScaling.denom, cifScaling.num]]];
.. adds the condition that cif/cifScaling is guaranteed to be an integer, so cif*cifScaling.denom MOD cifScaling.num = 0
ENDLOOP;
END;
CollectDirectives:
PROC =
BEGIN
GetDirectives:
PROC [p:
REF, lev:
CD.Level]
RETURNS [l:
LIST
OF
REF
ANY ←
NIL] =
BEGIN
IF p#
NIL
THEN
BEGIN
WITH p
SELECT
FROM
d: CIFDest => l ← LIST[NEW[CIFDirectiveRec ← [lev, d.cifDest, d.deltaRadius]]];
list: LIST OF REF ANY => l ← List.Append[GetDirectives[list.first, lev], GetDirectives[list.rest, lev]];
ENDCASE => l ← LIST[NEW[CIFDirectiveRec ← [lev, ToRope[p], 0]]];
END;
END;
cifDirectives ← NIL;
FOR levels:
LIST
OF
CD.Level ← design.technology.usedLevels, levels.rest
WHILE levels #
NIL
DO
cifDirectives ← List.Append[GetDirectives[CDProperties.GetPropFromLevel[levels.first, $CDxCIFName], levels.first ], cifDirectives];
ENDLOOP;
END;
WriteCIF:
PROC[ comm: CDSequencer.Command] =
BEGIN
ENABLE
BEGIN
ABORTED => {TerminalIO.WriteRope[" CIF generation aborted\n"]; GOTO Exit};
RopeNeeded =>
BEGIN
explanation: Rope.ROPE = IO.PutFR[format: "Please enter a string corresponding to the value %g: ", v1: IO.refAny[ref^]];
ref^ ← TerminalIO.RequestRope[explanation];
RESUME;
END;
UNWIND => NULL;
END;
assign world variable from sequencer
design ← comm.design;
initialize main object, layer names, resolution, cellCount, and draw records
mainCellPtr ← design.actual.first.specific;
mainOb^← [
p: NIL,
size: [x: infinity, y: infinity],
level: CD.FetchLevel[t: design.technology, uniqueKey: $NOcOL],
specificRef: mainCellPtr
];
cellCount ← 0;
stopFlag^ ← FALSE;
cifMeasureR.worldClip ←
CD.DesignRect[
x1: -infinity, y1: -infinity,
x2: infinity, y2: infinity];
cifMeasureR.drawRect ← cifMeasureArea;
clearIDsRec.worldClip ←
CD.DesignRect[
x1: -infinity, y1: -infinity,
x2: infinity, y2: infinity];
clearIDsRec.drawChild ← ClearCellIDs;
clearIDsRec.stopFlag ← stopFlag;
cifDrawRec.worldClip ←
CD.DesignRect[
x1: -infinity, y1: -infinity,
x2: infinity, y2: infinity];
cifDrawRec.drawRect ← cifOrArea;
Search world data structure: normalize coordinates,
set mainRect to design size, mark used levels
CDOps.DrawDesign[design, cifMeasureR];
get file name
TerminalIO.WriteRope["Please input name of CIF file\n"];
base ← TerminalIO.RequestRope["(Extension will be '.cif')\n", ">> "];
IF Rope.IsEmpty[base] THEN base ← "foo";
name ← Rope.Concat[base, ".cif"];
TerminalIO.WriteRope["The name of the output file is "];
TerminalIO.WriteRope[name];
TerminalIO.WriteRope["\n"];
cifFile ←
FS.StreamOpen[name, create !
FS.Error =>
IF error.group#bug THEN {
TerminalIO.WriteRope[error.explanation];
GOTO FileIOOpenFailed; }
];
TerminalIO.WriteRope["How many CIF units per lambda?\n"];
cifPerLambda ← TerminalIO.RequestInt[" (1 CIF unit = 0.01 microns) \n", ">> "];
IF debugging
THEN
BEGIN
TerminalIO.WriteRope["cif Per Lambda = "];
TerminalIO.WriteInt[cifPerLambda];
TerminalIO.WriteRope["\n"];
END;
cifPerCD ← cifPerLambda/lambda;
output header
cifFile.PutF["( %s );\n", IO.rope[design.name]];
cifFile.PutF["( generated %g by Xerox PARC ChipNDale and BrandyCIFter );\n", IO.time[]];
cifFile.PutF["( with lambda = %d CIF units );\n", IO.int[cifPerLambda]];
cifFile.PutF["( origin = [x: 0, y: 0], size = [x: %d, y: %d] CIF units );\n", IO.int[cifPerCD*(mainRect.x2-mainRect.x1)], IO.int[cifPerCD*(mainRect.y2-mainRect.y1)]];
ask for comment lines
TerminalIO.WriteRope["Comment line: \n"];
comment ← TerminalIO.RequestRope["(parens must balance, CR for no comment)\n", ">> "];
WHILE
NOT Rope.IsEmpty[comment]
DO
cifFile.PutF["( %s )\n", IO.rope[comment]];
comment ← NIL;
TerminalIO.WriteRope["Another comment line:\n"];
comment←TerminalIO.RequestRope[
"(parens must balance, CR for no further comments)\n", ">> "];
ENDLOOP;
IO.PutChar[cifFile,
IO.
CR];
CollectDirectives[];
SetCIFScaling[];
ClearCellIDs[design.actual.first.dummyCell, [0,0], 0, NIL];
CIFDefineObject[ mainOb];
define hybrid top level object
SymHeader[topLevelOb];
CIFSymbolCall[mainOb];
FOR directives:
LIST
OF
REF
ANY ← cifDirectives, directives.rest
WHILE directives#
NIL
DO
currentDirective ← NARROW[directives.first];
IF currentDirective.deltaRadius<0
THEN
BEGIN
cifLayerAnnounced ← FALSE;
MakeFlatCIF[RectProc: CIFRectOut];
END;
ENDLOOP;
SymTrailer[];
CIFSymbolCall[topLevelOb, CDOrient.ComposeOrient[CDOrient.rotate180, CDOrient.mirrorX] -- mirrorY --, [x: -cifPerCD*mainRect.x1, y: cifPerCD*mainRect.y2]];
cifFile.PutRope[";\nEnd ... \n"];
cifFile.Close[];
IF debugging THEN TerminalIO.WriteRope[" End of WriteCIF\n"];
TerminalIO.WriteRope["CIF file completed"];
EXITS
Exit => NULL;
FileIOOpenFailed => {TerminalIO.WriteRope["Open Failed\n"]};
END; -- of WriteCIF
MakeFlatCIF:
PROC [
RectProc:
PROC[rect: CIFRect]] =
BEGIN
AnalyzeTesselation:
PROC [ms: MaskStateRef] =
BEGIN
--the tesselation is in cif coordinates
active: CD.Rect = ms.areas.ContentsBound[rect: CDBasics.universe];
IF CDBasics.NonEmpty[active]
THEN {
IF currentDirective.deltaRadius # 0
THEN {
t: REF CornerStitching.Tesselation = ms.unbloated;
ms.unbloated ← ms.areas;
ms.areas ← t;
ms.areas.ChangeRect[rect: CDBasics.universe, newValue: NIL];
IF currentDirective.deltaRadius>0
THEN
[] ← ms.unbloated.EnumerateArea[rect: CDBasics.universe, perTile: BloatTile, data: ms]
ELSE {
ms.areas.ChangeRect[rect: active, newValue: $covered];
[] ← ms.unbloated.EnumerateArea[rect: CDBasics.universe, perTile: BloatTile, data: ms,
backgroundValue: $covered];
};
};
[] ← ms.areas.EnumerateArea[rect: CDBasics.universe,
perTile: OutputTile, data: ms, backgroundValue: NIL];
TerminalIO.WriteRope["."]; -- end of window
};
END;
OutputTile:
PROCEDURE [tile: CornerStitching.TilePtr, data:
REF
ANY]
=
-- CornerStitching.PerTileProc
BEGIN -- only called on tiles with non-NIL values
ms: MaskStateRef = NARROW[data];
cr: CIFRect = CDBasics.Intersection[tile.Area, ms.partClip];
RectProc[cr];
END;
designClip: CD.DesignRect;
cifClip: CD.Rect;
nRows, nCols: NAT;
ms: MaskStateRef =
NEW[ MaskState ← [
areas: CornerStitching.NewTesselation[],
unbloated: CornerStitching.NewTesselation[]
]];
dr: CD.DrawRef = CD.NewNullDeviceDrawRef[design];
dr.minimalSize ← 0;
dr.drawRect ← dr.saveRect ← NoteLevel;
dr.devicePrivate ← NEW[CD.DesignRect ← CDBasics.empty];
dr.worldClip ← CDBasics.universe;
CDOps.DrawDesign[design, dr]; -- measure design
designClip ← NARROW[dr.devicePrivate, REF CD.DesignRect]^;
cifClip ← Bloat[CDRectToCIF[designClip], ABS[currentDirective.deltaRadius]/nmPerCIF];
nRows ← Ceiling[num: cifClip.y2-cifClip.y1, denom: partHeight];
nCols ← Ceiling[num: cifClip.x2-cifClip.x1, denom: partWidth];
TerminalIO.WriteRope[IO.PutFR[format: ".. in %d rows and %d columns..", v1: IO.int[nRows], v2: IO.int[nCols]]];
dr.drawRect ← dr.saveRect ← NoteRectangle;
dr.devicePrivate ← ms;
TerminalIO.WriteRope[IO.PutFR["%s -> %s", IO.rope[ToRope[CD.LevelKey[currentDirective.cdSource]]], IO.rope[currentDirective.cifDest]]];
FOR col:
NAT
IN [0..nCols)
DO
FOR row:
NAT
IN [0..nRows)
DO
ms.areas.ChangeRect[rect: CDBasics.universe, newValue: NIL];
ms.partClip ← [
-- in cif space
x1: cifClip.x1+col*partWidth,
y1: cifClip.y1+row*partHeight,
x2: cifClip.x1+col*partWidth+MIN[partWidth, cifClip.x2-cifClip.x1-partWidth*col],
y2: cifClip.y1+row*partHeight+MIN[partHeight, cifClip.y2-cifClip.y1-partHeight*row]
];
dr.worldClip ← CIFRectToCD[CDBasics.Extend[ms.partClip, 1+ABS[currentDirective.deltaRadius]/nmPerCIF]];
CDViewer.ShowArrow[design: design,
pos: [x: (dr.worldClip.x1+dr.worldClip.x2)/2, y: (dr.worldClip.y1+dr.worldClip.y2)/2]]; -- keep user happy
CDOps.DrawDesign[design, dr]; -- build tesselation of the relevant design rectangle
AnalyzeTesselation[ms]; -- sends the current part to s
ENDLOOP;
ENDLOOP;
TerminalIO.WriteRope["+"];
CDViewer.RemoveArrow[design: design];
EXITS
END;
NoteLevel:
PROC [ r:
CD.DesignRect, l:
CD.Level, pr:
CD.DrawRef ] =
BEGIN
bb: REF CD.DesignRect = NARROW[pr.devicePrivate];
bb^ ← CDBasics.Surround[bb^, r];
END;
NoteRectangle:
PROC [r:
CD.DesignRect, l:
CD.Level, pr:
CD.DrawRef] =
BEGIN
ms: MaskStateRef = NARROW[pr.devicePrivate];
IF l=currentDirective.cdSource
AND CDBasics.NonEmpty[r]
THEN
ms.areas.ChangeRect[
rect: CDRectToCIF[r],
newValue: $covered
];
END;
BloatTile:
PROCEDURE [tile: CornerStitching.TilePtr, data:
REF
ANY]
=
-- CornerStitching.PerTileProc --
BEGIN
ms: MaskStateRef = NARROW[data];
cr: CD.Rect = CDBasics.Intersection[CDBasics.universe, Bloat[tile.Area, ABS[currentDirective.deltaRadius/nmPerCIF]]];
IF CDBasics.NonEmpty[cr]
THEN
ms.areas.ChangeRect[rect: cr, newValue: tile.Value];
END;
Bloat:
PROC [r:
CD.Rect, delta:
CD.Number]
RETURNS [br:
CD.Rect] =
INLINE
BEGIN
--Be careful not to exceed the limits of a CD.Number, even temporarily
br ← [x1: MAX[FIRST[CD.Number]+delta, r.x1]-delta,
y1: MAX[FIRST[CD.Number]+delta, r.y1]-delta,
x2: MIN[LAST[CD.Number]-delta, r.x2]+delta,
y2: MIN[LAST[CD.Number]-delta, r.y2]+delta]
END;
CDRectToCIF:
PROC [ cdr:
CD.DesignRect ]
RETURNS [ cifr: CIFRect ] =
{cifr ← CDBasics.NormalizeRect[
[x1: cifPerCD*cdr.x1,
y1: cifPerCD*cdr.y1,
x2: cifPerCD*cdr.x2,
y2: cifPerCD*cdr.y2]]};
CIFRectToCD:
PROC [ cifr: CIFRect ]
RETURNS [ cdr:
CD.DesignRect ] =
{cdr ← CDBasics.NormalizeRect[
[x1: cifr.x1/cifPerCD,
y1: cifr.y1/cifPerCD,
x2: cifr.x2/cifPerCD,
y2: cifr.y2/cifPerCD]]};
PrescaleCIF:
PROC [ cif: Rational ]
RETURNS [
INT ] =
BEGIN
n: INT = cif.num*cifScaling.denom;
d: INT = cif.denom*cifScaling.num;
IF ABS[n] MOD ABS[d] # 0 THEN ERROR CantRepresentExactly;
RETURN[n/d];
Ceiling:
PROC [num, denom:
CD.Number]
RETURNS [ c:
INT ] =
{c ← (num+denom-1)/denom};
ReduceTerms:
PROC [ r: Rational ]
RETURNS [ Rational ] =
BEGIN
gcd: INT = GCD[r.num, r.denom];
RETURN[[num: r.num/gcd, denom: r.denom/gcd]];
END;
GCD:
PROC [ m, n:
INT ]
RETURNS [
INT ] =
BEGIN
r: INT;
SELECT m
FROM
<0 => m ← -m;
ENDCASE => NULL;
SELECT n
FROM
<0 => n ← -n;
>0 => NULL;
ENDCASE => RETURN[m];
r ← m MOD n;
WHILE r>0 DO m ← n; n ← r; r ← m MOD n; ENDLOOP;
RETURN[n];
END;
CDSequencer.ImplementCommand[a: $WriteCif, p: WriteCIF];
CDMenus.CreateEntry[menu~$ProgramMenu, entry~"Hierarchical CIF", key~$WriteCif];
END. -- of BrandyCIFter