CDColorVersatecImpl:
CEDAR
PROGRAM
IMPORTS Atom, CD, CDBasics, CDCommandOps, CDMenus, CDViewer, CDOps, CDProperties, CDSequencer, CornerStitching, IO, PDFileWriter, Process, Real, TerminalIO =
BEGIN
Ink: TYPE = PDFileFormat.Toner; -- {black, cyan, magenta, yellow};
ColorRef: TYPE = REF ColorLoad←NIL;
ColorLoad:
TYPE =
ARRAY Ink
OF PDFileWriter.LoadReference;
-- a color associated with a level is a collection of textures associated with inks
whiteLoadRef: PDFileWriter.LoadReference ←
LAST[PDFileWriter.LoadReference];
-- used to identify tiles without toner so that you can avoid recording them
pdState: PDFileWriter.PDState;
pdx, pdy : CARDINAL ← 0; -- origin of band in pixel coords
cdx, cdy,
-- origin of band in chipndale coords
xoffset, yoffset : INT ← 0; -- offset in transforming between coord systems
plotScale:
REAL;
-- scale factor between systems, is negative for y-axis due to reversed direction of increasing values (when compared with pixel coordinates)
versatec: PDFileFormat.DeviceCode ← last;
??? PDFileWriter seems to require that some value be given; at the current time, there wasn't one for versatec so I chose the only unassigned value
sRes, fRes:
CARDINAL ← 200;
-- resolution (slow directition, fast direction) in pixels per inch
maxScanLineWidth:
CARDINAL = 8000;
--for the wide-bed Versatec, length in the "fast" direction
maxPixPerLambda: REAL = 20; -- maximum # of pixels to make lambda
overLap: CARDINAL = 300; -- number of pixels by which to overlap strips
scanLineWidth: CARDINAL ← maxScanLineWidth;
imageSSize: REAL ← 0;
imageFSize: CARDINAL ← maxScanLineWidth;
bandSSize:
CARDINAL ← 64;
-- number chosen because of buffer-size of versatec
debugging: BOOL ← FALSE;
numBands, numRectangles: INT ← 0;
PlotStateRef: TYPE = REF PlotState;
PlotState:
TYPE =
RECORD [
text: REF CornerStitching.Tesselation ← NIL,
tes: ARRAY CD.Level OF REF CornerStitching.Tesselation ← ALL[NIL],
-- enumerates all the rectangles in a given level
scale: REAL ← 1.0, -- pixels per CD.DesignNumber
totalPlotClip, bandClip:
CD.DesignRect ← [0,0,0,0],
rectangles in design space touching all geometry in the plot, or this band
colorLoads: REF ARRAY CD.Level OF ColorRef
];
StripFileName:
PROC [index:
INTEGER]
RETURNS [name: Rope.
ROPE] =
BEGIN
name ← IO.PutFR[ "///temp/plot%d.pd", IO.int[index+1]];
END;
VersatecColorPlotComm:
PROC [ comm: CDSequencer.Command ] =
BEGIN
TerminalIO.WriteRope["Colorplot\n"];
[] ← CDCommandOps.CallWithResource[ProtectedPlotDesign, comm, $ColorPlot, abortPlot];
END;
signalFont: Graphics.FontRef ← NIL;
plotter: Rope.ROPE ← "vice";
plotFileToSpool: Rope.ROPE ← "[vice]<sysdir>plot.pd";
plotFileOnPlotter: Rope.ROPE ← "<sysdir>plot.pd";
plotFileOnLocalMachine: Rope.ROPE ← "plot0.pd";
spoolToPlotter: BOOL ← TRUE;
abortPlot: REF BOOL = NEW[BOOL ← FALSE];
ProtectedPlotDesign:
PROC [comm: CDSequencer.Command] =
BEGIN
design: CD.Design = comm.design;
plotClip: CD.DesignRect = CDBasics.ToRect[comm.pos, comm.sPos];
s: IO.STREAM ← NIL;
n: INT ← TerminalIO.RequestInt["How many vertical strips? [1..10] "];
strips: INT ← MAX[1, MIN[10, n]];
BEGIN
ENABLE {
-- for ERRORs
UNWIND => {
s ← AbortFile[s];
CDViewer.RemoveArrow[design];
TerminalIO.WriteRope[" ** plot aborted ** "]
};
};
dr: CD.DrawRef = CD.NewNullDeviceDrawRef[design];
scale:
REAL ←
MIN[maxPixPerLambda/
CD.lambda, (
REAL[scanLineWidth]+
REAL[scanLineWidth-overLap]*(strips-1))/(plotClip.x2-plotClip.x1)];
Center the x range of the selected area of the design on the plotter bed, with at most maxPixPerLambda pixels per lambda. If multiple strips are called for, overlap adjacent ones by "overLap" pixels.
-- total number of pixels across plot = REAL[scanLineWidth]+REAL[scanLineWidth-overLap]*(strips-1))
-- total width of design = plotClip.x2-plotClip.x1
ps: PlotStateRef =
NEW[ PlotState ← [
text: CornerStitching.NewTesselation[],
scale: scale,
totalPlotClip: plotClip,
colorLoads: NEW[ARRAY CD.Level OF ColorRef←ALL[NIL]]
] ];
usedLevels: REF PACKED ARRAY CD.Level OF BOOL ← NIL;
imageSSize ← (plotClip.y2-plotClip.y1)* scale;
-- length of plot <in slow direction> in pixels
plotScale ← scale;
IF debugging
THEN {
TerminalIO.WriteRope[" Scale factor (pixels/design#)*100 --> "];
TerminalIO.WriteInt[Real.RoundLI[plotScale*100]];
TerminalIO.WriteLn[];
};
TerminalIO.WriteRope["Starting Color Versatec plot "];
TerminalIO.WriteLn[];
TRUSTED {Process.SetPriority[Process.priorityBackground]};
dr.minimalSize ← 0;
dr.drawRect ← dr.saveRect ← NoteLevel;
dr.worldClip ← CDBasics.universe;
dr.stopFlag ← abortPlot;
dr.devicePrivate ← usedLevels ← NEW[PACKED ARRAY CD.Level OF BOOL ← ALL[FALSE]];
CDOps.DrawDesign[design, dr]; -- mark used levels
dr.drawRect ← dr.saveRect ← NoteRectangle;
dr.devicePrivate ← ps;
FOR strip: INT IN [0..strips) DO
-- Determine clip rectangle for strip
clip:
CD.DesignRect = [
x1: plotClip.x1+strip*((plotClip.x2-plotClip.x1)/strips),
y1: plotClip.y1,
x2: plotClip.x1+(strip+1)*((plotClip.x2-plotClip.x1)/strips)+
Real.Fix[overLap/scale]+1,
y2: plotClip.y2];
tonerSet: PDFileWriter.TonerSet ← ALL[TRUE];
localFileName: Rope.ROPE = StripFileName[strip];
TerminalIO.WriteRope["Recording strip on file "];
TerminalIO.WriteRope[localFileName];
TerminalIO.WriteLn[];
pdState ← PDFileWriter.Create[ fileName: localFileName, deviceCode: versatec, sResolution: sRes, fResolution: fRes, imageSSize: Real.RoundC[imageSSize + 1], imageFSize: imageFSize, bandSSize: bandSSize, leftOverMode: FALSE];
ps.colorLoads^ ← ALL[NIL];
numBands ← 0;
numRectangles ← 0;
PDFileWriter.StartImage[pdState: pdState, toners: tonerSet];
-- For each band in the strip
FOR topLine:
INT ← 0, topLine+bandSSize
WHILE topLine<imageSSize
DO
x1, y1, x2, y2: REAL;
dc: CD.DesignRect;
IF abortPlot^ THEN GOTO AbortPlot;
-- Determine coordinate transformations
pdx ← 0;
pdy ← topLine;
cdx ← clip.x1;
cdy ← clip.y2 - Real.Fix[topLine*(1./scale)];
xoffset ← pdx - Real.Fix[cdx*scale];
yoffset ← pdy - Real.Fix[cdy*(-1*scale)];
-- (negative scale to account for CD increasing y north, PD increasing y going south)
x1 ← (-1 - xoffset )/scale;
y1 ← ((pdy + bandSSize +1) - yoffset)/(-scale);
x2 ← (scanLineWidth - xoffset)/scale;
y2 ← (pdy - yoffset)/(-scale);
dc ← CDBasics.NormalizeRect[[x1: Real.Fix[x1], y1: Real.Fix[y1], x2: Real.Fix[x2], y2: Real.Fix[y2]]] ;
ps.bandClip ← dr.worldClip ← [x1:
MAX[plotClip.x1, dc.x1-1], y1:
MAX[plotClip.y1, dc.y1-1], x2:
MIN[plotClip.x2, dc.x2+1], y2:
MIN[plotClip.y2, dc.y2+1]];
-- rectangle in design space that ChipNDale can use to clip its recursive drawing
IF debugging
THEN {
TerminalIO.WriteLn;
TerminalIO.WriteRope[" pdy: "];
TerminalIO.WriteInt[pdy];
TerminalIO.WriteLn;
TerminalIO.WriteRope[" CD band: "];
TerminalIO.WriteInt[ps.bandClip.x1];
TerminalIO.WriteInt[ps.bandClip.y1];
TerminalIO.WriteInt[ps.bandClip.x2];
TerminalIO.WriteInt[ps.bandClip.y2];
TerminalIO.WriteLn
};
--Display current band under consideration to pacify user
CDViewer.ShowArrow[design: design,
pos: [x: (dr.worldClip.x1+dr.worldClip.x2)/2, y: (dr.worldClip.y1+dr.worldClip.y2)/2]];
FOR l:
CD.Level
IN
CD.Level
DO
IF ps.tes[l]#
NIL
THEN
-- clear previous tessalations
CornerStitching.ChangeRect[plane: ps.tes[l], rect: CDBasics.universe, newValue: NIL];
ENDLOOP;
CDOps.DrawDesign[design, dr]; -- build tesselations of the relevant design rectangle
AnalyzeTesselations[ps];
-- assign each level a texture and draw all the rectangles in the region
numBands ← numBands + 1;
TerminalIO.WriteRope["."];
ENDLOOP; -- for each band in the strip
PDFileWriter.EndPage[pdState];
PDFileWriter.Close[pdState];
TerminalIO.WriteRope["*\n"];
ENDLOOP; -- for each strip
TerminalIO.WriteRope[" finished plot(s)\n"];
IF debugging
THEN {
TerminalIO.WriteRope["# bands -> "];
TerminalIO.WriteInt[numBands];
TerminalIO.WriteRope[" # rectangles -> "];
TerminalIO.WriteInt[numRectangles];
TerminalIO.WriteLn;
};
CDViewer.RemoveArrow[design: design];
EXITS
AbortPlot => {
s ← AbortFile[s];
CDViewer.RemoveArrow[design];
TerminalIO.WriteRope[" ** plot aborted ** "];
};
END; -- enable
END; -- PlotDesign
NoteLevel:
PROC [ r:
CD.DesignRect, l:
CD.Level, pr:
CD.DrawRef ] =
BEGIN
ps: REF PACKED ARRAY CD.Level OF BOOL = NARROW[pr.devicePrivate];
ps[l] ← TRUE;
END;
NoteRectangle:
PROC [ r:
CD.DesignRect, l:
CD.Level, pr:
CD.DrawRef ] =
BEGIN
ps: PlotStateRef = NARROW[pr.devicePrivate];
IF CDBasics.NonEmpty[r]
THEN {
IF ps.tes[l]=NIL THEN ps.tes[l] ← CornerStitching.NewTesselation[];
ps.tes[l].ChangeRect[rect: r, newValue: $covered];
};
END;
TonerKey:
PROC [toner: PDFileFormat.Toner]
RETURNS [
ATOM] =
INLINE {
RETURN [
SELECT toner
FROM
black => $CDxVersatecBlack,
cyan => $CDxVersatecCyan,
magenta => $CDxVersatecMagenta,
yellow => $CDxVersatecYellow,
ENDCASE => $CDxVersatecBlack
]
};
AnalyzeTesselations:
PROC [ ps: PlotStateRef ] =
BEGIN
FOR lev:
CD.Level
IN
CD.Level
DO
IF ps.tes[lev]#
NIL
THEN {
IF ps.colorLoads^[lev]=
NIL
THEN {
tex: REF;
ps.colorLoads^[lev] ← NEW[ColorLoad];
FOR toner: Ink
IN Ink
DO
tex ← CDProperties.GetPropFromLevel[from: lev, prop: TonerKey[toner]];
ps.colorLoads^[lev][toner] ←
(IF tex = NIL THEN whiteLoadRef ELSE MakeLoadref[tex]);
ENDLOOP;
};
FOR toner: Ink
IN Ink
DO
IF ps.colorLoads^[lev][toner] = whiteLoadRef
THEN
PDFileWriter.SetColorOff[pdState, toner]
ELSE
PDFileWriter.SetColorTile[pdState, toner, ps.colorLoads^[lev][toner], transparent];
ENDLOOP;
[] ← ps.tes[lev].EnumerateArea[
rect: ps.bandClip,
perTile: ProcessTile,
data: ps,
backgroundValue: $none
];
};
ENDLOOP;
END;
ProcessTile:
PROCEDURE [tile: CornerStitching.TilePtr, data:
REF
ANY]
-- CornerStitching.PerTileProc -- =
BEGIN -- only called on covered tiles
rightBound, x1, y1, x2, y2: INT;
pdx1, pdx2, pdy1, pdy2 : CARDINAL;
sSize, fSize : CARDINAL;
t: CornerStitching.TilePtr;
ps: PlotStateRef = NARROW[data];
tileValue: REF = tile.Value;
IF tileValue = $covered
THEN
BEGIN
r: CD.DesignRect = tile.Area;
-- Calculate absolute coords of rectangle here as offset from plot origin
x1 ← Real.RoundLI[xoffset + r.x1*plotScale];
y1 ← Real.RoundLI[yoffset + r.y1*(-plotScale)];
x2 ← Real.RoundLI[xoffset + r.x2*plotScale];
y2 ← Real.RoundLI[yoffset + r.y2*(-plotScale)];
rightBound ← Real.RoundLI[xoffset + ps.bandClip.x2*plotScale];
-- if rectangle is in bandarea, clip and write; otherwise disregard rectangle
IF
NOT ((( x1 > rightBound)
OR (x2 < 0))
OR
(( y1 < pdy) OR (y2 > MIN[imageSSize, pdy+bandSSize]))) THEN {
pdx1 ← MAX[x1, 0];
pdx2 ← MIN[x2, rightBound, imageFSize];
pdy1 ← MIN[y1, pdy+bandSSize];
pdy2 ← MAX[y2, pdy];
fSize ← pdx2 - pdx1;
sSize ← pdy1 - pdy2;
-- can't have rectangles landing on or over the imageSSize
IF pdy1 >= imageSSize
THEN
sSize ← Real.Fix[imageSSize - pdy2] ;
-- disregard rectangles without area
IF ((sSize # 0)
AND (fSize # 0))
THEN {
PDFileWriter.MaskRectangle[pdState: pdState, sMin: pdy2, fMin: pdx1, sSize: sSize, fSize: fSize];
TerminalIO.WriteRope["+"];
numRectangles ← numRectangles + 1;
IF debugging
THEN {
TerminalIO.WriteRope[" position -> "];
TerminalIO.WriteInt[Real.RoundLI[y1]];
TerminalIO.WriteInt[Real.RoundLI[x1]];
TerminalIO.WriteRope[" size -> "];
TerminalIO.WriteInt[sSize];
TerminalIO.WriteInt[fSize];
TerminalIO.WriteLn;
}
}
}
END;
-- code for outlining rectangles
Put lines at boundaries with regions of other color
FOR t ← tile.NEastNeighbour, t.WSouthNeighbour WHILE tile.SouthEdge<t.NorthEdge DO
going south along east edge of tile
IF t.Value#tileValue THEN
BEGIN
ps.context[black].SetCP[x: tile.EastEdge, y: MAX[tile.SouthEdge, t.SouthEdge]];
ps.context[black].DrawTo[x: tile.EastEdge, y: MIN[tile.NorthEdge, t.NorthEdge]];
END;
ENDLOOP;
FOR t ← tile.ENorthNeighbour, t.SWestNeighbour WHILE tile.WestEdge<t.EastEdge DO
going west along north edge of tile
IF t.Value#tileValue THEN
BEGIN
ps.context[black].SetCP[x: MAX[tile.WestEdge, t.WestEdge], y: tile.NorthEdge];
ps.context[black].DrawTo[x: MIN[tile.EastEdge, t.EastEdge], y: tile.NorthEdge];
END;
ENDLOOP;
END;
SignalValueRef: TYPE = REF SignalValue;
SignalValue: TYPE = RECORD [s: Rope.ROPE];
LookForSignalName:
PROC [ design:
CD.Design, aptr:
CD.ApplicationPtr, x:
REF ]
RETURNS [done:
BOOL←
FALSE, removeMe:
BOOL←
FALSE, include:
CD.ApplicationList←
NIL,
repaintMe:
BOOL←
FALSE, repaintInclude:
BOOL←
FALSE]
-- CDCallSpecific.CallProc -- =
BEGIN
This area needs considerable improvement. The ultimate goal is to find a place to put the signal name where it will not be confused with any other geometry (than the named piece, or continuations on the same layer) or signal name.
GetSignalName:
PROC [signal:
REF]
RETURNS [s: Rope.
ROPE] =
BEGIN
WITH signal
SELECT
FROM
atom: ATOM => s ← Atom.GetPName[atom];
rope: Rope.ROPE => s ← rope;
ENDCASE => s ← "?";
END;
ps: PlotStateRef = NARROW[x];
r: CD.DesignRect = CDBasics.Intersection[ps.totalPlotClip, CDApplications.ApplicationRect[aptr]];
signal: REF = CDProperties.GetProp[from: aptr, prop: $SignalName];
IF signal # NIL AND CDBasics.NonEmpty[r] THEN
BEGIN
signalValue: SignalValueRef = NEW[SignalValue ← [s: GetSignalName[signal]]];
others: LIST OF REF CornerStitching.Region = NARROW[ps.text.EnumerateArea[rect: r]];
ps.text.ChangeRect[rect: r, newValue: signalValue];
FOR other: LIST OF REF CornerStitching.Region ← others, other.rest WHILE other#NIL DO
ps.text.ChangeRect[rect: other.first.rect, newValue: other.first.value];
ENDLOOP;
END
ELSE done ← FALSE;
END;
PaintASignalName:
PROC [tile: CornerStitching.TilePtr, data:
REF
ANY]
RETURNS [
REF
ANY]
-- CornerStitching.PerTileProc -- =
BEGIN
ps: PlotStateRef = NARROW[data];
textContext: Graphics.Context = ps.vPatch[black].CopyContext[];
signalValue: SignalValueRef = NARROW[tile.Value[]];
dx, dy, xmin, xmax, ymin, ymax: REAL;
r: CD.DesignRect = CDBasics.Intersection[tile.Area, ps.totalPlotClip];
ctr: CD.DesignPosition = CDBasics.Center[r];
[dx: dx, dy: dy] ← Graphics.Map[sc: ps.context[black], dc: ps.vPatch,
sx: ctr.x, sy: ctr.y];
[xmin: xmin, ymin: ymin, xmax: xmax, ymax: ymax] ← Graphics.RopeBox[font: signalFont, rope: signalValue.s];
textContext.Translate[tx: dx-(xmax+xmin)/2, ty: dy-(ymax+ymin)/2]; -- centers the text in r
IF ((xmax-xmin)>(ymax-ymin)) # ((r.x2-r.x1)>(r.y2-r.y1)) THEN
textContext.Rotate[270];
textContext.SetColor[Graphics.white];
textContext.DrawBox[[xmin: -2, ymin: -2, xmax: xmax-xmin+2, ymax: ymax-ymin+2]];
leave a two-scan-line white patch all around
textContext.SetColor[Graphics.black];
textContext.SetCP[x: 0, y: 0];
textContext.DrawRope[rope: signalValue.s, font: signalFont];
RETURN[data];
END;
Stipple16: TYPE = ARRAY[0..16) OF CARDINAL;
Stipple8: TYPE = ARRAY[0..8) OF [0..256);
Stipple4: TYPE = ARRAY[0..4) OF [0..16);
ToTexture:
PROC [pattern:
REF
ANY]
RETURNS [texture: Stipple16] =
--tries to convert pattern to a texture stipple
BEGIN
IF pattern=NIL THEN RETURN[Stipple16[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]; -- should not occur
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]
END;
MakeLoadref:
PROC [pattern:
REF
ANY]
RETURNS [PDFileWriter.LoadReference] =
TRUSTED BEGIN
loadRef: PDFileWriter.LoadReference;
texture: Stipple16 ← ToTexture[pattern];
loadRef ← PDFileWriter.LoadContiguousColorTile[pdState: pdState, phase: 0, sMin: 0, fMin: 0, sSize: 16, fSize: 16, bitsPtr: @texture];
RETURN[loadRef];
END;
AbortFile:
PROC [s:
IO.
STREAM ]
RETURNS [
IO.
STREAM ] =
{IF s#NIL THEN s.Close[abort: TRUE]; RETURN[NIL]};
Init:
PROC ~ {
CDSequencer.ImplementCommand[a~$VersatecColorPlot, p~VersatecColorPlotComm];
signalFont ← VFonts.GraphicsFont[VFonts.EstablishFont[family: "Helvetica", size: 18, bold: TRUE]];
CDMenus.CreateEntry[$RectProgramMenu, "Color Plot", $VersatecColorPlot];
TerminalIO.WriteRope["ChipNDale COLOR Versatec plotter loaded\n"];
};
Init[];
END. -- of CDColorVersatecImpl