<> <> <> <> <> <> DIRECTORY TerminalIO USING [RequestRope, RequestInt, WriteRope, WriteInt], CDOrient, CD USING [Level, LevelKey, Rect, FetchLevel, lambda, ObPtr, CellPtr, Number, Position, DesignNumber, DesignPosition, DesignRect, Application, ApplicationPtr, ApplicationList, CellRecord, Orientation, ObjectDefinition, Properties, Design, CreateDrawRef, DrawRectProc, DrawRef, DrawProc, DrawInformation], CDIO, 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, IsEmpty, FromRefText], Atom USING [GetPropFromList, PutPropOnList, GetPName], CDApplications USING [ARectO], CDBasics USING [empty, universe, Extend, NormalizeRect, NonEmpty, Intersection, Surround, Center], CDCommandOps, CDOps USING [DrawDesign], CDProperties USING [GetPropFromLevel, PutPropOnObject], CDSequencer USING [Command, ImplementCommand], CDViewer USING [ShowArrow, RemoveArrow]; BrandyCIFter: CEDAR PROGRAM IMPORTS List, CornerStitching, TerminalIO, CDIO, CDMenus, CDCommandOps, 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 _ FALSE; -- 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]; clearIDsRec: CD.DrawRef; cifMeasureR: CD.DrawRef; cifDrawRec: CD.DrawRef; --for cifOrArea 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/ 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>> 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; <> <<--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; <> 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 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]; EXITS NoHelp => ERROR; 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 _ CD.CreateDrawRef[design]; cifMeasureR.interestClip _ CDBasics.universe; cifMeasureR.drawRect _ cifMeasureArea; clearIDsRec _ CD.CreateDrawRef[design]; clearIDsRec.interestClip _ CDBasics.universe; clearIDsRec.drawChild _ ClearCellIDs; clearIDsRec.stopFlag _ stopFlag; cifDrawRec _ CD.CreateDrawRef[design: design]; <<--used as a parameter to drawMe in order to call cifOrArea>> cifDrawRec.interestClip _ CDBasics.universe; 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 ("]; TerminalIO.WriteRope[CDIO.GetWorkingDirectory[design]]; TerminalIO.WriteRope[") "]; base _ TerminalIO.RequestRope[">> "]; IF Rope.IsEmpty[base] THEN base _ design.name; IF Rope.IsEmpty[base] THEN base _ "foo"; name _ CDIO.MakeName[base: base, ext: ".cif", wDir: CDIO.GetWorkingDirectory[design]]; TerminalIO.WriteRope["Output file will be "]; 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 cifPerLambda<2 THEN { TerminalIO.WriteRope["cif Per Lambda value not reasonable\n"]; RETURN }; IF debugging THEN { TerminalIO.WriteRope["cif Per Lambda = "]; TerminalIO.WriteInt[cifPerLambda]; TerminalIO.WriteRope["\n"]; }; 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.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\n"]; EXITS Exit => NULL; FileIOOpenFailed => {TerminalIO.WriteRope["Open Failed\n"]}; END; -- of WriteCIF <<--Flat CIF code>> <<>> 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.CreateDrawRef[design]; dr.minimalSize _ 0; dr.drawRect _ dr.saveRect _ NoteLevel; dr.devicePrivate _ NEW[CD.DesignRect _ CDBasics.empty]; dr.interestClip _ 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.interestClip _ CIFRectToCD[CDBasics.Extend[ms.partClip, 1+ABS[currentDirective.deltaRadius]/nmPerCIF]]; CDViewer.ShowArrow[design: design, pos: [x: (dr.interestClip.x1+dr.interestClip.x2)/2, y: (dr.interestClip.y1+dr.interestClip.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]; END; 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; WriteCIFComm: PROC [comm: CDSequencer.Command] = BEGIN TerminalIO.WriteRope["Hierarchical CIF output\n"]; [] _ CDCommandOps.CallWithResource[WriteCIF, comm, $CIF, stopFlag]; END; CDSequencer.ImplementCommand[a: $WriteCif, p: WriteCIFComm, queue: doQueue]; CDMenus.CreateEntry[menu: $ProgramMenu, entry: "Hierarchical CIF", key: $WriteCif]; END.