CDPDPlotImpl.mesa
Copyright © 1985, 1986, 1987 by Xerox Corporation. All rights reserved.
History
Black and white versatec plot written by: E. McCreight, August 1, 1983
Changed for color Versatec by: Kim Rachmeler, 1983
Rewritten by: Christian Jacobi, April 11, 1985 3:21:48 pm PST
Last Edited by: Christian Jacobi, November 9, 1987 10:17:19 am PST
DIRECTORY
Atom,
CD,
CDBasics,
CDCells,
CDDefaultProcs,
CDEnvironment,
CDImports,
CDInstances,
CDIO,
CDVArrow,
CDViewer,
CDCurves,
CDOps,
CDPDPlot,
CDProperties,
CDPropertyTools,
CDSequencer,
CDValue,
Commander,
CStitching,
IO,
PeachPrint,
PDFileFormat,
PDFileWriter,
Process,
Real,
Rope,
RuntimeError USING [UNCAUGHT],
TerminalIO,
ViewerClasses,
ViewerSpecs;
CDPDPlotImpl:
CEDAR
MONITOR
IMPORTS Atom, CD, CDBasics, CDCells, CDDefaultProcs, CDEnvironment, CDImports, CDInstances, CDIO, CDValue, CDVArrow, CDViewer, CDOps, CDProperties, CDPropertyTools, CDSequencer, Commander, CStitching, IO, PDFileWriter, PeachPrint, Process, Real, Rope, RuntimeError, TerminalIO
EXPORTS CDPDPlot =
BEGIN
--MONITOR: to protect the stipples
retry: CONDITION;
plottingInProgress: BOOL ← FALSE;
Enter:
ENTRY
PROC [d:
CD.Design] = {
ENABLE UNWIND => NULL;
BROADCAST retry; --to make others call CheckAborted
WHILE plottingInProgress
DO
WAIT retry;
CDSequencer.CheckAborted[d]; --UNWIND IS CATCHED !!!
ENDLOOP;
plottingInProgress ← TRUE
};
Leave:
ENTRY
PROC [] = {
ENABLE UNWIND => NULL;
plottingInProgress ← FALSE;
BROADCAST retry
};
lastTechPropKey: REF ← $CDxLastTechnology; --a property of the dd.stippleKey
abortPlot: REF BOOL ← NEW[BOOL ← FALSE];
--handling colors
Toner: TYPE = PDFileFormat.Toner; -- {black, cyan, magenta, yellow, .. 15};
TonerKeys: TYPE = ARRAY Toner OF REF;
LoadArray: TYPE = ARRAY Toner OF PDFileWriter.LoadReference;
ColorDescription: TYPE = ARRAY CD.Layer OF REF LoadArray ← ALL[NIL];
noColor: PDFileWriter.LoadReference =
LAST[PDFileWriter.LoadReference];
-- used to identify tiles without toner so that you can avoid recording them
fullColor: PDFileWriter.LoadReference = noColor-1;
-- used to identify tiles with full toner so that you can avoid recording them
tonerToKeyKeys:
REF TonerKeys =
NEW[TonerKeys ←
ALL[
NIL]];
--gives a key to get the keys to get the color stipple as property of the layers
--device description
DeviceDesc: TYPE = CDPDPlot.DeviceDesc;
TaskDesc: TYPE = CDPDPlot.TaskDesc;
PageMode: TYPE = {fixedPage, finiteStripe, anyStripe};
Stipple4: TYPE = ARRAY[0..4) OF [0..16);
Stipple8: TYPE = ARRAY[0..8) OF [0..256);
Stipple16: TYPE = ARRAY[0..16) OF CARDINAL;
fiddleDevice: PUBLIC REF DeviceDesc ← NIL;
error: ERROR = CODE;
MakeDevice:
PUBLIC
PROC [key:
ATOM]
RETURNS [dd:
REF DeviceDesc] = {
ColorVersatec:
PROC [dd:
REF DeviceDesc] = {
dd.deviceCode ← last; --does not yet has an assigned value, use "last"
dd.sResolution ← 200; -- resolution (slow directition), pixels / inch
dd.fResolution ← 200; -- resolution (fast direction), pixels / inch
dd.imageFSize ← 8000; --for the wide-bed Versatec, length in the "fast" direction;
dd.toners ← ALL[FALSE];
dd.toners[black] ← TRUE;
dd.toners[cyan] ← TRUE;
dd.toners[magenta] ← TRUE;
dd.toners[yellow] ← TRUE;
dd.bandSSize ← 64; -- number chosen because of buffer-size of versatec
dd.overlap ← 300; -- number of pixels by which to overlap strips
dd.maxPixPerLambda ← 20; -- maximum # of pixels to make lambda
dd.pageMode ← anyStripe;
dd.leftOverMode ← FALSE;
dd.stippleKey ← $CDxVersatec;
dd.name ← "color versatec";
};
InkJetC150:
PROC [dd:
REF DeviceDesc] = {
dd.deviceCode ← VAL[10];
dd.sResolution ← 120; -- resolution (slow directition), pixels / inch
dd.fResolution ← 120; -- resolution (fast direction), pixels / inch
dd.imageFSize ← 1020;
dd.toners ← ALL[FALSE];
dd.toners[black] ← TRUE;
dd.toners[cyan] ← TRUE;
dd.toners[magenta] ← TRUE;
dd.toners[yellow] ← TRUE;
dd.bandSSize ← 48; --
dd.overlap ← 30; -- number of pixels by which to overlap strips
dd.maxPixPerLambda ← 20; -- maximum # of pixels to make lambda
dd.pageMode ← finiteStripe;
dd.pageSlowSize ← 1020;
dd.leftOverMode ← FALSE;
dd.stippleKey ← $CDxVersatec;
dd.name ← "ink jet";
};
Color400:
PROC [dd:
REF DeviceDesc] = {
dd.deviceCode ← VAL[9];
dd.sResolution ← 400; -- resolution (slow directition), pixels / inch
dd.fResolution ← 400; -- resolution (fast direction), pixels / inch
dd.imageFSize ← 4096;
dd.toners ← ALL[FALSE];
dd.toners[black] ← TRUE;
dd.toners[cyan] ← TRUE;
dd.toners[magenta] ← TRUE;
dd.toners[yellow] ← TRUE;
dd.bandSSize ← 50; --
dd.overlap ← 30; -- number of pixels by which to overlap strips
dd.maxPixPerLambda ← 240; -- maximum # of pixels to make lambda
dd.pageMode ← fixedPage;
dd.pageSlowSize ← 5500;
dd.leftOverMode ← FALSE;
dd.stippleKey ← $CDxC400;
dd.name ← "Color400";
};
Puffin:
PROC [dd:
REF DeviceDesc] = {
dd.deviceCode ← PDFileFormat.DeviceCode[puffin];
dd.sResolution ← 384; -- resolution (slow directition), pixels / inch
dd.fResolution ← 384; -- resolution (fast direction), pixels / inch
dd.imageFSize ← dd.fResolution * 17 / 2;
dd.toners ← ALL[FALSE];
dd.toners[black] ← FALSE;
dd.toners[cyan] ← TRUE;
dd.toners[magenta] ← TRUE;
dd.toners[yellow] ← TRUE;
dd.bandSSize ← 16; --
dd.overlap ← 30; -- number of pixels by which to overlap strips
dd.maxPixPerLambda ← 20; -- maximum # of pixels to make lambda
dd.pageMode ← fixedPage;
dd.pageSlowSize ← dd.sResolution*11;
dd.leftOverMode ← TRUE;
dd.stippleKey ← $CDxPuffin;
dd.name ← "Puffin";
};
PlateMaker:
PROC [dd:
REF DeviceDesc] = {
dd.deviceCode ← PDFileFormat.DeviceCode[last];
dd.sResolution ← 1200; -- resolution (slow directition), pixels / inch
dd.fResolution ← 1200; -- resolution (fast direction), pixels / inch
dd.imageFSize ← dd.fResolution * 17 / 2;
dd.toners ← ALL[FALSE];
dd.toners[black] ← TRUE;
dd.bandSSize ← 16; --
dd.overlap ← 30; -- number of pixels by which to overlap strips
dd.maxPixPerLambda ← 20; -- maximum # of pixels to make lambda
dd.pageMode ← fixedPage;
dd.pageSlowSize ← dd.sResolution*11;
dd.leftOverMode ← TRUE;
dd.stippleKey ← $CDxPDPlotPlatemaker;
dd.name ← "platemaker [color separation]";
};
Raven384:
PROC [dd:
REF DeviceDesc] = {
dd.deviceCode ← PDFileFormat.DeviceCode[raven];
dd.sResolution ← 384; -- resolution (slow directition), pixels / inch
dd.fResolution ← 384; -- resolution (fast direction), pixels / inch
dd.imageFSize ← dd.fResolution * 17 / 2;
dd.toners ← ALL[FALSE];
dd.toners[black] ← TRUE;
dd.bandSSize ← 16; --
dd.overlap ← 30; -- number of pixels by which to overlap strips
dd.maxPixPerLambda ← 20; -- maximum # of pixels to make lambda
dd.pageMode ← fixedPage;
dd.pageSlowSize ← dd.sResolution*11;
dd.leftOverMode ← TRUE;
dd.stippleKey ← $CDxPDPlotRaven384;
dd.name ← "Raven384 [color separation]";
};
dd ← NEW[DeviceDesc];
dd.contextFilter ← NEW[CD.ContextFilter←ALL[TRUE]];
SELECT key
FROM
$Versatec, $PDPlotVersatec => ColorVersatec[dd];
$C150, $PDPlotC150 => InkJetC150[dd];
$Puffin, $PDPlotPuffin => Puffin[dd];
$Platemaker, $PDPlotPlatemaker => PlateMaker[dd];
$Raven384, $PDPlotRaven384 => Raven384[dd];
$Color400, $PDPlotColor400 => Color400[dd];
$UserDevice, $PDPlotUserDevice => dd ← fiddleDevice;
ENDCASE => ERROR error;
};
--state of plot
PlotState: TYPE = REF PlotStateRec;
PlotStateRec:
TYPE =
RECORD [
tes: ARRAY CD.Layer OF CStitching.Tesselation ← ALL[NIL],
plotScale: REAL ← 1.0, -- pixels per CD.Number
plotClip: CD.Rect ← [0, 0, 0, 0], --in design space touching all geometry in plot
stripeClip: CD.Rect ← [0, 0, 0, 0],--in design space touching all geometry in stripe
bandClip:
CD.Rect ← [0, 0, 0, 0],
--in design space touching all geometry in band
--plot and stripe areas are defined in integral cd units
--the band is defined in pixel units;
-- bandClip contains at least all area contributing to the real band
pdState: PDFileWriter.PDState,
colorLoads: REF ColorDescription,
anouncedLayer: INT ← -1,
design: CD.Design,
band:
CD.Rect ← [0, 0, 0, 0],
-- in device pixel coordinates but y-sign inversion not yet done
-- low border points are legal pixels of band, high border points are not
dd: REF DeviceDesc
];
ToDeviceX:
PROC[ps: PlotState, cdX:
CD.Number]
RETURNS [
REAL] =
INLINE {
--but not clipped!
RETURN [(cdX-ps.stripeClip.x1)*ps.plotScale]
};
ToDeviceY:
PROC[ps: PlotState, cdY:
CD.Number]
RETURNS [
REAL] =
INLINE {
--but not clipped!
RETURN [(cdY-ps.stripeClip.y1)*ps.plotScale]
};
ToDeviceR:
PROC[ps: PlotState, r:
CD.Rect]
RETURNS [
CD.Rect] =
INLINE {
--but not clipped!
RETURN [[
x1: Real.Round[ToDeviceX[ps, r.x1]],
y1: Real.Round[ToDeviceY[ps, r.y1]],
x2: Real.Round[ToDeviceX[ps, r.x2]],
y2: Real.Round[ToDeviceY[ps, r.y2]]
]]
};
ConvertY2:
PROC[ps: PlotState, y2:
CD.Number]
RETURNS [
INT] =
INLINE {
y2 in (non negated y) pixel coordinates; does the conversion of the y
RETURN [ps.dd.imageSSize-1-y2]
};
Plot:
PUBLIC
PROC [task:
REF TaskDesc] = {
ps: PlotState;
clipSize: CD.Position;
dd: REF DeviceDesc ← task.dd;
design: CD.Design ← task.design;
instance: CD.Instance ← task.instance;
pageStep: CD.Position;
pageWidth: CD.Position;
dr: CD.DrawRef;
IF design=NIL OR dd=NIL THEN ERROR;
BEGIN
ENABLE {
-- for ERRORs
UNWIND => {
CDVArrow.RemoveArrow[design];
TerminalIO.PutRope[" ** plot aborted **\n"];
Leave[];
};
};
Enter[design];
TRUSTED {Process.SetPriority[Process.priorityBackground]};
task.strips ← MAX[1, MIN[10, task.strips]];
IF task.abort=NIL THEN task.abort ← NEW[BOOL←FALSE];
IF ~CDBasics.NonEmpty[task.clip] THEN task.clip ← CDOps.BoundingBox[design];
clipSize ← CDBasics.SizeOfRect[task.clip];
dr ←
CD.CreateDrawRef[[
design: design,
selections: FALSE,
borders: TRUE,
fontSubstitution: task.substituteFonts,
stopFlag: task.abort,
drawRect: NoteRectangle,
drawChildSel: CDDefaultProcs.IgnoreChild,
drawOutLine: DrawOutline,
contextFilter: dd.contextFilter
]];
SetStipples[dd, design.technology];
CheckSpecialScale[task];
--Center the x range of the selected area of the design on the plotter bed, with at most dd.maxPixPerLambda pixels per lambda. If multiple task.strips are called for, overlap adjacent ones by "dd.toners" pixels.
-- total number of pixels across plot = imageFSize+[imageFSize-overlap]*(strips-1))
ps ←
NEW[PlotStateRec ← [
plotClip: task.clip,
colorLoads: NEW[ColorDescription ← ALL[NIL]],
design: design,
plotScale: task.scale,
dd: dd
]];
IF ps.plotScale<=0
THEN {
ps.plotScale ←
MIN[
REAL[dd.maxPixPerLambda] / design.technology.lambda,
REAL[(dd.imageFSize-dd.overlap)*task.strips+dd.overlap] / clipSize.x
]
};
dd.imageSSize ← Real.InlineRoundC[clipSize.y*ps.plotScale]+1;
IF dd.pageMode=fixedPage
THEN {
dd.imageSSize ← MIN[dd.imageSSize, dd.pageSlowSize];
ps.plotScale ← MIN[ps.plotScale, REAL[dd.pageSlowSize]/(clipSize.y+1)];
};
pageStep ← [(clipSize.x+task.strips-1)/task.strips, clipSize.y];
pageWidth ← [Real.Round[dd.imageFSize/ps.plotScale]+1, clipSize.y]; --larger to prevent rounding errors
dr.devicePrivate ← ps;
TerminalIO.PutRopes["start ploting\n"];
FOR strip:
INT
IN [0..task.strips)
DO
fileName: Rope.ROPE ← MakeName[task.fileName, strip, task.strips];
ps.stripeClip ← [
--but not yet intersected with complete design clip
x1: ps.plotClip.x1+strip*pageStep.x,
y1: ps.plotClip.y1,
x2: ps.plotClip.x1+strip*pageStep.x+pageWidth.x,
y2: ps.plotClip.y2
];
ps.pdState ← PDFileWriter.Create[
fileName: fileName,
deviceCode: dd.deviceCode,
sResolution: dd.sResolution,
fResolution: dd.fResolution,
imageSSize: (IF dd.pageMode=fixedPage THEN dd.pageSlowSize ELSE dd.imageSSize),
imageFSize: dd.imageFSize,
bandSSize: dd.bandSSize,
leftOverMode: dd.leftOverMode,
maxLoadWords: dd.maxLoadWords,
copies: dd.copies
];
CDDefaultProcs.ConvertContextsToDeviceRects[
pr: dr,
dDrawRect: NoteDeviceRectangle,
dOrigin: [ps.stripeClip.x1, ps.stripeClip.y1],
scale: ps.plotScale
];
ps.colorLoads^ ← ALL[NIL];
PDFileWriter.StartImage[pdState: ps.pdState, toners: dd.toners];
-- For each band in the strip
FOR bandYtop:
INT ← dd.imageSSize, bandYtop-dd.bandSSize
WHILE bandYtop>0
DO
--bandYtop is top pixel not plotted !
dc: CD.Rect; --task.clip for band in design coordinates; little larger than band
CDSequencer.CheckAborted[design];
-- Determine coordinate transformations
dc ← [
x1: ps.stripeClip.x1-1,
y1: ps.stripeClip.y1+Real.Round[(bandYtop-dd.bandSSize)/ps.plotScale]-2,
x2: ps.stripeClip.x1+pageWidth.x+1,
y2: ps.stripeClip.y1+Real.Round[bandYtop/ps.plotScale]+2
];
dr.interestClip ← ps.bandClip ← CDBasics.Intersection[ps.plotClip, dc];
ps.band ← [
x1: 0,
y1: MAX[0, bandYtop-dd.bandSSize],
x2: MIN[Real.Round[ToDeviceX[ps, ps.bandClip.x2]+1], ps.dd.imageFSize],
y2: bandYtop
];
--Display current band under consideration to pacify user
IF instance=NIL THEN CDVArrow.ShowArrow[design, CDBasics.Center[dr.interestClip]];
-- clear previous tessalations
ps.anouncedLayer ← -1;
FOR l:
CD.Layer
IN
CD.Layer
DO
IF ps.tes[l]#
NIL
THEN
CStitching.ChangeRect[plane: ps.tes[l], rect: CDBasics.universe, new: NIL];
ENDLOOP;
-- build tesselations of design rectangles
IF instance=
NIL
THEN CDOps.DrawDesign[design, dr]
ELSE CD.DrawOb[dr, instance.ob, instance.trans, instance.properties];
AnalyzeTesselations[ps]; -- actually draw tesselations
TerminalIO.PutRope["."];
ENDLOOP; -- each band in the strip
PDFileWriter.EndPage[ps.pdState];
PDFileWriter.Close[ps.pdState];
TerminalIO.PutRopes["*\nrecorded strip on file ", fileName, "\n"];
ENDLOOP; -- each strip
TerminalIO.PutRope[" finished plot\n"];
CDVArrow.RemoveArrow[design: design];
Leave[];
END;
};
MaskRect:
PROC [ps: PlotState, r:
CD.Rect] = --INLINE--
{
--coordinates in devices pixel coordinates, but y not yet negated;
--plots inclusive low, exclusive high border pixels
debugBand: CD.Rect ← ps.band;
debugR: CD.Rect ← r;
IF CDBasics.Intersect[r, ps.band]
THEN {
r ← CDBasics.Intersection[r, ps.band];
IF r.x2>r.x1
AND r.y2>r.y1
THEN
PDFileWriter.MaskRectangle[pdState: ps.pdState, sMin: ConvertY2[ps, r.y2-1], fMin: r.x1, sSize: r.y2-r.y1, fSize: r.x2-r.x1];
}
};
ProcessTile:
PROC [tile: CStitching.Tile, data:
REF
ANY] = {
IF tile.value=$covered
THEN {
ps: PlotState = NARROW[data];
MaskRect[ps, CStitching.Area[tile]]
};
};
AnalyzeTesselations:
PROC [ps: PlotState] = {
FOR lev:
CD.Layer
IN
CD.Layer
DO
IF ps.tes[lev]#
NIL
THEN {
SetColor[ps, lev];
CStitching.EnumerateArea[plane: ps.tes[lev], rect: ps.band, eachTile: ProcessTile, data: ps, skip: NIL];
};
ENDLOOP;
};
NoteRectangle:
PROC [pr:
CD.DrawRef, r:
CD.Rect, l:
CD.Layer] = {
--r in cd coords
ps: PlotState = NARROW[pr.devicePrivate];
IF ps.dd.contextFilter[l]
THEN {
r ← CDBasics.Intersection[r, pr.interestClip];
IF CDBasics.NonEmpty[r]
THEN {
r ← ToDeviceR[ps, r];
IF ps.tes[l]=NIL THEN ps.tes[l] ← CStitching.NewTesselation[];
CStitching.ChangeRect[plane: ps.tes[l], rect: r, new: $covered];
};
};
};
NoteDeviceRectangle:
PROC [pr:
CD.DrawRef, r:
CD.Rect, l:
CD.Layer] = {
--r in device pixel coords; before changing y sign
ps: PlotState = NARROW[pr.devicePrivate];
IF ps.dd.contextFilter[l]
THEN {
r ← CDBasics.Intersection[r, ps.band];
IF CDBasics.NonEmpty[r]
THEN {
IF ps.tes[l]=NIL THEN ps.tes[l] ← CStitching.NewTesselation[];
CStitching.ChangeRect[plane: ps.tes[l], rect: r, new: $covered];
};
};
};
DrawOutline:
CD.DrawRectProc = {
--PROC [r: Rect, l: Layer, pr: DrawRef]
delta: INT = 8;--doesn't cause overflow, plot scale is limited
ps: PlotState = NARROW[pr.devicePrivate];
--restrict outline to be only slightly larger than device, to prevent arithmetic overflow
r.x1 ← MAX[pr.interestClip.x1-delta, r.x1]; --r in cd Coordinates
r.y1 ← MAX[pr.interestClip.y1-delta, r.y1];
r.x2 ← MIN[pr.interestClip.x2+delta, r.x2];
r.y2 ← MIN[pr.interestClip.y2+delta, r.y2];
--convert to device pixel coordinates
r ← ToDeviceR[ps, r];
--draw [doesn't draw 2 border pixels]
NoteDeviceRectangle[pr, [r.x1, r.y1, r.x2+1, r.y1+1], l]; --bottom
NoteDeviceRectangle[pr, [r.x1, r.y2-1, r.x2+1, r.y2], l]; --top
NoteDeviceRectangle[pr, [r.x1, r.y1, r.x1+1, r.y2+1], l]; --left
NoteDeviceRectangle[pr, [r.x2-1, r.y1, r.x2, r.y2+1], l]; --right
};
--command level
CheckSpecialScale:
PROC [task:
REF TaskDesc] = {
micronsPerInch: REAL ~ 25400;
specialScale: REAL ← -1; --means microns on plotted device per lambda
WITH CDProperties.GetAtomProp[task.dd.stippleKey, $CDxPDPlotScale]
SELECT
FROM
ri: REF INT => specialScale ← ri^;
rr: REF REAL => specialScale ← rr^;
ENDCASE => specialScale ← -1;
IF specialScale>0
THEN {
size: CD.Position ← CDBasics.SizeOfRect[task.clip];
task.scale ← specialScale*task.dd.sResolution/micronsPerInch/task.design.technology.lambda;
TerminalIO.PutF["plotting at special scale %g microns/lambda [%g pixels/chipndaleUnit]\n", [real[specialScale]], [real[task.scale]]];
IF task.dd.pageMode=fixedPage
THEN {
IF task.scale >
REAL[task.dd.pageSlowSize]/(size.y+1)
THEN
TerminalIO.PutRope["doesn't fit on page; must change scale\n"];
};
};
};
RequestClipRect:
PROC [comm: CDSequencer.Command]
RETURNS [clip:
CD.Rect, inst:
CD.Instance ←
NIL] = {
n: INT;
n ← TerminalIO.RequestSelection[header: "plot area",
choice: LIST["plot complete design", "plot drawn rectangle", "plot like viewer", "plot area of selection", "plot selection only"]
];
SELECT n
FROM
1 => {
clip ← CDBasics.Extend[CDOps.BoundingBox[comm.design], 2];
TerminalIO.PutRope["plot complete design\n"];
};
2 => {
clip ← CDBasics.ToRect[comm.pos, comm.sPos];
TerminalIO.PutRope["plot rectangle of command\n"];
};
3 => {
clip ← CDViewer.VisibleRect[CDViewer.GetViewer[comm]];
TerminalIO.PutRope["plot as viewed\n"];
};
4 => {
clip ← CDBasics.Extend[CDOps.BoundingBox[comm.design, TRUE], 2];
TerminalIO.PutRope["plot area of selection\n"];
};
5 => {
sel: CD.InstanceList ← NIL;
design: CD.Design ← comm.design;
TerminalIO.PutRope["plot selection only\n"];
FOR l:
CD.InstanceList ← CDOps.InstList[design], l.rest
WHILE l#
NIL
DO
IF l.first.selected THEN sel ← CONS[l.first, sel]
ENDLOOP;
IF sel=
NIL
THEN {
TerminalIO.PutRope["no selection\n"];
ERROR ABORTED
};
inst ← CDInstances.NewInst[CDCells.CreateCellXTransformed[il: sel]];
clip ← CDBasics.Extend[CDInstances.InstRectO[inst], 2];
};
ENDCASE => ERROR ABORTED;
IF ~CDBasics.NonEmpty[clip]
THEN {
TerminalIO.PutRope["clip rectangle is empty\n"];
ERROR ABORTED
};
};
SubstituteFonts:
PROC []
RETURNS [substituteFonts:
BOOL] = {
n: INT;
n ← TerminalIO.RequestSelection[header: "substitute tioga fonts",
headerDoc: "do for schematics; don't for layout",
choice: LIST["yes", "no"]
];
substituteFonts ← n#2
};
IgnoreCommentLayer:
PROC []
RETURNS [ignoreComment:
BOOL] = {
n: INT;
n ← TerminalIO.RequestSelection[header: "suppress comment layer",
headerDoc: "do for large layouts; don't for anything else",
choice: LIST["yes", "no"]
];
ignoreComment ← n=1
};
Stripes:
PROC [allowStripes:
BOOL]
RETURNS [n:
INT𡤁] = {
IF allowStripes
THEN {
choice: INT;
choice ← TerminalIO.RequestSelection[header: "plot multiple vertical strips",
headerDoc: "it's ok not to...",
choice: LIST["yes", "no"]
];
IF choice=1
THEN {
n ← TerminalIO.RequestInt["How many vertical strips? [1..10] "];
IF n<1
OR n>10
THEN {
TerminalIO.PutRope["unreasonable number of stripes\n"];
ERROR ABORTED
};
};
};
};
cnt: INT ← 0;
PDColorPlotComm:
PROC [comm: CDSequencer.Command] = {
ENABLE error => {
SELECT TerminalIO.RequestSelection["ERROR",
LIST["abort plot", "reset and abort plot", "debug"]]
FROM
1 => GOTO tobad;
2 => {currTech ← NIL; GOTO tobad};
3 => REJECT;
ENDCASE => GOTO tobad;
};
clip: CD.Rect;
mustSendToDevice: BOOL ← FALSE;
server: Rope.ROPE; copies: INT ← 1;
task: REF TaskDesc ← NEW[TaskDesc ← [design: comm.design, abort: abortPlot, strips: 1]];
TerminalIO.PutRope["Color plot\n"];
task.dd ← MakeDevice[comm.key];
IF task.dd=
NIL
THEN {
TerminalIO.PutRope["no device specified\n"];
RETURN
};
TerminalIO.PutRopes[task.dd.name, "\n"];
IF CDImports.HasUnloadedImports[comm.design].yes
THEN {
TerminalIO.PutRope["design has unloaded imports\n"];
IF ~TerminalIO.Confirm["unloaded imports, continue anyway", "you would better load imports first", 20] THEN ERROR ABORTED;
};
--plot parameters
[clip, task.instance] ← RequestClipRect[comm];
task.substituteFonts ← SubstituteFonts[];
IF IgnoreCommentLayer[] THEN task.dd.contextFilter[CD.commentLayer] ← FALSE;
task.strips ← Stripes[task.dd.pageMode=anyStripe];
--direct sending to servers
BEGIN
default: Rope.ROPE ← NIL;
choice: LIST OF Rope.ROPE ← LIST["no", "type name of server"];
IF comm.key=$PDPlotVersatec THEN default ← "Sleepy"
ELSE IF comm.key=$PDPlotColor400 THEN default ← "MtFuji";
IF default#NIL THEN choice.rest.rest ← LIST[default];
SELECT TerminalIO.RequestSelection["send immediately to server", choice]
FROM
0, 1 => NULL;
2 => {
mustSendToDevice ← TRUE;
server ← TerminalIO.RequestRope["server name>"];
};
3 => {
mustSendToDevice ← TRUE;
server ← default;
};
ENDCASE => NULL;
IF mustSendToDevice
THEN {
IF TerminalIO.Confirm["multiple copies"]
THEN
copies ← TerminalIO.RequestInt["copies>"];
IF copies<1
OR copies>10
THEN {
TerminalIO.PutRope["unreasonable number of copies\n"];
ERROR ABORTED
};
};
END;
IF clip.x1>=clip.x2
OR clip.y1>=clip.y2
THEN {
TerminalIO.PutRope["**cannot plot empty area\n"];
ERROR ABORTED
};
task.clip ← clip;
IF mustSendToDevice
THEN
task.fileName ← IO.PutFR["///temp/chipndale/pdplots/Plot%gX.pd", IO.int[cnt ← cnt+1]];
Plot[task];
IF mustSendToDevice
THEN {
CDEnvironment.ExecFileEntry[key: "PeachPrint"]; --should load file; PeachPrint loaded only on demand
FOR strip:
INT
IN [0..task.strips)
DO
PeachPrint.DoPeachPrintCommand[
server: server,
file: MakeName[task.fileName, strip, task.strips],
log: TerminalIO.TOS[],
copies: copies,
fork: TRUE
!
PeachPrint.PupAborted => {TerminalIO.PutRope["**Pup aborted plotting\n"]; CONTINUE};
RuntimeError.UNCAUGHT => {TerminalIO.PutRope["**Peach aborted plotting\n"]; CONTINUE};
];
ENDLOOP
};
EXITS tobad => NULL
};
MakeName:
PROC [base: Rope.
ROPE, strip, strips:
INT𡤁]
RETURNS [fileName: Rope.
ROPE] = {
modifier: Rope.ROPE ← IF strips>1 THEN IO.PutFR["%d", IO.int[strip+1]] ELSE NIL;
IF Rope.IsEmpty[base] THEN base ← "PDPlot";
fileName ← CDIO.MakeName[wDir: "///temp/", base: base, modifier: modifier, ext: "pd"];
};
WaitPlotFinishedCommand: Commander.CommandProc = {
prio: Process.Priority = Process.GetPriority[];
IF ~plottingInProgress
THEN {
IO.PutRope[cmd.out, " waiting for plot to start"];
WHILE ~plottingInProgress
DO
Process.Pause[Process.SecondsToTicks[1]];
ENDLOOP;
IO.PutRope[cmd.out, ". plot started...\n"]
};
Process.SetPriority[Process.priorityBackground];
WHILE plottingInProgress
DO
Process.Pause[Process.SecondsToTicks[2]];
ENDLOOP;
Process.SetPriority[prio];
IO.PutRope[cmd.out, " finished\n"]
};
--interpreter set up
blackX: REF ← NIL;
cyanX: REF ← NIL;
yellowX: REF ← NIL;
magentaX: REF ← NIL;
currTech: CD.Technology ← NIL;
currLayer: CD.Layer ← CD.undefLayer;
Forgett:
PROC [l:
CD.Layer] = {
IF blackX#NIL THEN CDProperties.PutLayerProp[onto: l, prop: blackX, val: NIL];
IF cyanX#NIL THEN CDProperties.PutLayerProp[onto: l, prop: cyanX, val: NIL];
IF magentaX#NIL THEN CDProperties.PutLayerProp[onto: l, prop: magentaX, val: NIL];
IF yellowX#NIL THEN CDProperties.PutLayerProp[onto: l, prop: yellowX, val: NIL];
};
Start:
PUBLIC PROC[tech, device, b, c, m, y:
ATOM, text: Rope.
ROPE←
NIL, invert:
BOOL←
FALSE] = {
IF currTech#NIL THEN ERROR error;
CDPropertyTools.RemoveProperties[$CDxPDPlotScale];
CDPropertyTools.RemoveProperties[device];
CDProperties.PutAtomProp[device, $CDxPDPlotScale, NIL]; --might be not associated
IF b#NIL THEN CDPropertyTools.RemoveProperties[b];
IF c#NIL THEN CDPropertyTools.RemoveProperties[c];
IF m#NIL THEN CDPropertyTools.RemoveProperties[m];
IF y#NIL THEN CDPropertyTools.RemoveProperties[y];
currTech ← CD.FetchTechnology[tech];
blackX ← b;
cyanX ← c;
yellowX ← y;
magentaX ← m;
CDValue.Store[currTech, device, text];
IF invert
THEN {
CDProperties.PutAtomProp[device, tonerToKeyKeys[black], yellowX];
CDProperties.PutAtomProp[device, tonerToKeyKeys[cyan], magentaX];
CDProperties.PutAtomProp[device, tonerToKeyKeys[yellow], blackX];
CDProperties.PutAtomProp[device, tonerToKeyKeys[magenta], cyanX];
}
ELSE {
CDProperties.PutAtomProp[device, tonerToKeyKeys[black], blackX];
CDProperties.PutAtomProp[device, tonerToKeyKeys[cyan], cyanX];
CDProperties.PutAtomProp[device, tonerToKeyKeys[yellow], yellowX];
CDProperties.PutAtomProp[device, tonerToKeyKeys[magenta], magentaX];
};
CDProperties.PutAtomProp[device, $CDxLastTechnology, currTech.key];
FOR l: CD.Layer IN CD.Layer DO Forgett[l] ENDLOOP;
};
End:
PUBLIC PROC [] = {
currTech ← NIL
};
Layer:
PUBLIC PROC [uniqueKey:
ATOM] = {
currLayer ← CD.FetchLayer[t: currTech, uniqueKey: uniqueKey];
Forgett[currLayer]
};
LayerNumber:
PUBLIC PROC [layer:
NAT] = {
currLayer ← layer;
Forgett[currLayer]
};
Color1:
PUBLIC
PROC [key:
ATOM] = {
CDProperties.PutLayerProp[onto: currLayer, prop: key, val: $ink]
};
Color4:
PUBLIC PROC [key:
ATOM, i0, i1, i2, i3: [0..16)] = {
CDProperties.PutLayerProp[onto: currLayer, prop: key,
val:
IF i0=0 AND i1=0 AND i2=0 AND i3=0 THEN NIL
ELSE IF i0=15 AND i1=15 AND i2=15 AND i3=15 THEN $ink
ELSE NEW[Stipple4 ← [i0, i1, i2, i3]]
]
};
Color8:
PUBLIC PROC [key:
ATOM, i0, i1, i2, i3, i4, i5, i6, i7: [0..256)] = {
CDProperties.PutLayerProp[onto: currLayer, prop: key,
val:
IF i0=0 AND i1=0 AND i2=0 AND i3=0 AND i4=0 AND i5=0 AND i6=0 AND i7=0 THEN NIL
ELSE IF i0=255 AND i1=i0 AND i2=i0 AND i3=i0 AND i4=i0 AND i5=i0 AND i6=i0 AND i7=i0 THEN $ink
ELSE NEW[Stipple8 ← [i0, i1, i2, i3, i4, i5, i6, i7]]
]
};
--initializations
Init:
PROC [] = {
CDPropertyTools.Associate[$CDxPDPlotScale, $CDxPDPlotScale];
tonerToKeyKeys[black] ← $CDxPDPlotBlack;
tonerToKeyKeys[cyan] ← $CDxPDPlotCyan;
tonerToKeyKeys[magenta] ← $CDxPDPlotMagenta;
tonerToKeyKeys[yellow] ← $CDxPDPlotYellow;
CDSequencer.ImplementCommand[key: $PDPlotVersatec, proc: PDColorPlotComm, queue: doQueue];
CDSequencer.ImplementCommand[key: $PDPlotC150, proc: PDColorPlotComm, queue: doQueue];
CDSequencer.ImplementCommand[key: $PDPlotColor400, proc: PDColorPlotComm, queue: doQueue];
CDSequencer.ImplementCommand[key: $PDPlotUserDevice, proc: PDColorPlotComm, queue: doQueue];
Commander.Register[
key: "///Commands/CDWaitPlotFinished",
proc: WaitPlotFinishedCommand,
doc: "waits until ChipNDale pd plotting is finished once"
];
TerminalIO.PutRope["ChipNDale PD color plot program loaded\n"];
};
SetStipples:
PROC [dd:
REF DeviceDesc, technology:
CD.Technology] = {
PrepareContextFilter:
PROC [dd:
REF DeviceDesc] = {
FOR l:
CD.Layer
IN
CD.Layer
DO
IF dd.contextFilter^[l]
THEN {
color: BOOL ← FALSE;
FOR toner: Toner
IN Toner
DO
IF dd.tonerToKey[toner]#
NIL
THEN
IF CDProperties.GetLayerProp[l, dd.tonerToKey[toner]]#
NIL
THEN {
color ← TRUE; EXIT
};
ENDLOOP;
dd.contextFilter^[l] ← color
}
ENDLOOP;
};
IF dd.stippleKey=
NIL
THEN {
TerminalIO.PutRope["device has undefined stipple key\n"];
ERROR ABORTED
};
IF technology.key#CDProperties.GetAtomProp[dd.stippleKey, lastTechPropKey]
THEN {
TerminalIO.PutRope["try to load the stipples\n"];
CDEnvironment.ExecFileEntry[Rope.Cat["PDPlot-stipples-", Atom.GetPName[dd.stippleKey], "-", technology.name], NIL, NIL];
TerminalIO.PutRope["stipples loaded; please check whether any errors occured\n"];
};
WITH CDValue.Fetch[technology, dd.stippleKey]
SELECT
FROM
r: Rope.ROPE => TerminalIO.PutRopes["color stipples used: [", r, "]\n"];
ENDCASE => {
TerminalIO.PutRope["**color stipples are not defined\n"];
ERROR ABORTED
};
IF technology.key#CDProperties.GetAtomProp[dd.stippleKey, lastTechPropKey]
THEN {
TerminalIO.PutRope["**Warning: another technology registered in the meantime; the technology independent stipples are redefined\n"];
};
dd.tonerToKey ← NEW[TonerKeys ← ALL[NIL]];
FOR t: PDFileWriter.Toner
IN PDFileWriter.Toner
DO
dd.tonerToKey^[t] ← CDProperties.GetAtomProp[dd.stippleKey, tonerToKeyKeys[t]];
IF ~dd.toners[t]
OR dd.tonerToKey^[t]=
NIL
THEN {
dd.toners[t] ← FALSE;
dd.tonerToKey^[t] ← NEW[INT];
}
ENDLOOP;
PrepareContextFilter[dd];
};
SetColor:
PROC [ps: PlotState, lev:
CD.Layer] = {
MakeLoadref:
PROC [pattern:
REF
ANY]
RETURNS [loadRef: PDFileWriter.LoadReference] =
TRUSTED {
ToTexture:
PROC [pattern:
REF
ANY]
RETURNS [texture: Stipple16] =
CHECKED {
--tries to convert pattern to a texture stipple
WITH pattern
SELECT
FROM
s16: REF Stipple16 => texture ← s16^;
s8:
REF Stipple8 =>
FOR i: [0..8)
IN [0..8)
DO
texture[i] ← texture[i+8] ← (256+1)*s8[i];
ENDLOOP;
s4:
REF Stipple4 =>
FOR i: [0..4)
IN [0..4)
DO
texture[i] ← texture[i+4] ← texture[i+8] ← texture[i+12] ← s4[i]*1111H;
ENDLOOP;
ENDCASE => RETURN[Stipple16[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0FFFFH]]; -- error texture
RETURN [texture]
}; -- ToTexture
IF pattern=NIL THEN loadRef ← noColor
ELSE IF pattern=$ink THEN loadRef ← fullColor
ELSE {
texture: Stipple16 ← ToTexture[pattern];
loadRef ← PDFileWriter.LoadContiguousColorTile[pdState: ps.pdState, phase: 0, sMin: 0, fMin: 0, sSize: 16, fSize: 16, bitsPtr: @texture];
IF loadRef>=fullColor THEN ERROR;
};
}; -- MakeLoadref
MakeLoad:
PROC [ps: PlotState, lev:
CD.Layer] =
INLINE {
--makes the load if it does not already exist
IF ps.colorLoads^[lev]=
NIL
THEN {
ps.colorLoads^[lev] ← NEW[LoadArray];
FOR toner: Toner
IN Toner
DO
tex: REF ← CDProperties.GetLayerProp[from: lev, prop: ps.dd.tonerToKey[toner]];
IF tex=NIL THEN ps.colorLoads^[lev][toner] ← noColor
ELSE ps.colorLoads^[lev][toner] ← MakeLoadref[tex]
ENDLOOP;
};
}; --MakeLoad
--SetColor
IF ps.anouncedLayer=lev THEN RETURN;
IF ps.colorLoads^[lev]=NIL THEN MakeLoad[ps, lev];
FOR toner: Toner
IN Toner
DO
IF ps.colorLoads^[lev][toner] = noColor
THEN
PDFileWriter.SetColorOff[ps.pdState, toner]
ELSE
IF ps.colorLoads^[lev][toner] = fullColor
THEN
PDFileWriter.SetColorInk[ps.pdState, toner]
ELSE
PDFileWriter.SetColorTile[ps.pdState, toner, ps.colorLoads^[lev][toner], transparent];
ENDLOOP;
ps.anouncedLayer ← lev
};
Init[];
END.