VersatecImpl:
CEDAR MONITOR
IMPORTS Atom, CD, CDApplications, CDCallSpecific, CDDialogue, CDExtras, CDInline, CDOps, CDProperties, CDSequencer, CornerStitching, FS, Graphics, GraphicsOps, IO, Menus, Process, Real, STPStreams, TerminalIO, VFonts, ViewerOps
EXPORTS =
BEGIN
maxScanLineWidth: CARDINAL = 7040; -- the wide-bed Versatec
scanLineWidth: CARDINAL ← maxScanLineWidth;
maxPatchHeight: CARDINAL = 144; -- fits in 64k
stippleRepeat: CARDINAL = 16;
Nibble: TYPE = [0..16);
ScanLine: TYPE = PACKED ARRAY [0..0) OF WORD;
ScanLineInNibbles: TYPE = PACKED ARRAY [0..0) OF Nibble;
CodedScanLine:
TYPE =
RECORD [
codeWordCount: CARDINAL ← 0,
codeWords: REF CodeWords ];
CodeWords: TYPE = PACKED ARRAY [0..maxScanLineWidth) OF CodeWord;
CodeWord:
TYPE =
MACHINE DEPENDENT RECORD [
repeats (0: 0..3): [1..16],
pattern (0: 4..7): Nibble];
textures: ARRAY CD.Level OF REF GraphicsOps.Texture ← ALL[NIL];
PlotStateRef: TYPE = REF PlotState;
PlotState:
TYPE =
RECORD [
text: REF CornerStitching.Tesselation ← NIL,
tes: ARRAY CD.Level OF REF CornerStitching.Tesselation ← ALL[NIL],
bitmap: GraphicsOps.BitmapRef ← NIL,
vPatch, context: Graphics.Context ← NIL,
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
texture: GraphicsOps.Texture ← ALL[0]
];
currentFlavor: ViewerClasses.ViewerFlavor = $ChipndaleVersatecPlot;
Init:
ENTRY PROC ~ {
menu: Menus.Menu = Menus.CreateMenu[];
Menus.AppendMenuEntry[menu: menu,
entry: Menus.CreateEntry[name: "AbortPlot", proc: AbortProc, guarded: TRUE]];
ViewerOps.RegisterViewerClass[flavor: currentFlavor,
class: NEW[ViewerClasses.ViewerClassRec ← [paint: PaintVersatecStripe, menu: menu]]];
CDSequencer.ImplementCommand[a~$VPlotLocal, p~PlotDesignToLocalFile];
CDSequencer.ImplementCommand[a~$VPlotRemote, p~PlotDesignToRemoteFile];
CDSequencer.ImplementCommand[a~$VPlotPlotter, p~PlotDesignToPlotter];
CDSequencer.ImplementCommand[a~$VPlot, p~PlotDesignToAny];
signalFont ← VFonts.GraphicsFont[VFonts.EstablishFont[family: "Helvetica", size: 18, bold: TRUE]];
TerminalIO.WriteRope["ChipNDale Versatec plotter loaded\n"];
};
PlotDesignToAny:
ENTRY PROC [ c: CDSequencer.Command ] =
BEGIN ENABLE UNWIND => NULL;
n:
CARDINAL ← TerminalIO.RequestSelection[
label: "Versatec Plot",
choice: LIST[" plotter", " local", " read name", " remote"]
];
SELECT n
FROM
1 => PlotDesign[c: c, to: plotter];
2 => PlotDesign[c: c, to: localFile];
3 => PlotDesign[c: c, to: requestFilename];
4 => {
TerminalIO.WriteRope["remote is not yet implemented\n"];
PlotDesign[c: c, to: remoteFile];
};
ENDCASE => TerminalIO.WriteRope["skipped\n"];
END;
PlotDesignToLocalFile:
ENTRY PROC [ c: CDSequencer.Command ] =
BEGIN ENABLE UNWIND => NULL;
PlotDesign[c: c, to: localFile];
END;
PlotDesignToRemoteFile:
ENTRY PROC [ c: CDSequencer.Command ] =
BEGIN ENABLE UNWIND => NULL;
PlotDesign[c: c, to: remoteFile];
END;
PlotDesignToPlotter:
ENTRY PROC [ c: CDSequencer.Command ] =
BEGIN ENABLE UNWIND => NULL;
PlotDesign[c: c, to: plotter];
END;
signalFont: Graphics.FontRef ← NIL;
viewer: ViewerClasses.Viewer ← NIL;
plotter: Rope.ROPE ← "vice";
plotFileToSpool: Rope.ROPE ← "[vice]<sysdir>plot.bits";
plotFileOnPlotter: Rope.ROPE ← "<sysdir>plot.bits";
plotFileOnLocalMachine: Rope.ROPE ← "plot.bits";
plotFileOnServer: Rope.ROPE ← "[Luther.alpine]<McCreight.pa>plot.bits";
latestPlotFile: Rope.ROPE ← NIL;
spoolToPlotter: BOOL ← TRUE;
abortPlot: BOOL ← FALSE;
PlotDesign:
INTERNAL PROC [ c: CDSequencer.Command, to: {localFile, remoteFile, plotter, requestFilename}] =
BEGIN
s: IO.STREAM ← NIL;
strips: [1..100] ← TerminalIO.RequestInt["How many vertical strips in the plot? "];
BEGIN ENABLE -- for ERRORs
BEGIN
???x.Error =>
BEGIN
TerminalIO.WriteRope[];
GOTO AbortPlot;
END;
UNWIND => {s ← AbortFile[s]; TerminalIO.WriteRope[" ** plot aborted ** "]};
END;
design: CD.Design = c.design;
plotClip: CD.DesignRect = CDInline.ToRect[c.pos, c.sPos];
dr: CD.DrawRef = CD.NewNullDeviceDrawRef[design];
scale:
REAL =
MIN[32.0/
CD.lambda, (
REAL[scanLineWidth]+
REAL[scanLineWidth-100]*(strips-1))/(plotClip.x2-plotClip.x1)];
Center the x range of the selected area of the design on the plotter bed, with at most 32 pixels per lambda. If multiple strips are called for, overlap adjacent ones by 100 pixels.
patchHeight:
CARDINAL = stippleRepeat*((
MIN[maxPatchHeight,
CARDINAL[Real.Fix[50*
CD.lambda*scale]]]+stippleRepeat-1)/stippleRepeat);
divisible by stippleRepeat, so that textures work out
bitmap: GraphicsOps.BitmapRef = GraphicsOps.NewBitmap[width: scanLineWidth, height: patchHeight];
ps: PlotStateRef = NEW[ PlotState ← [
text: CornerStitching.NewTesselation[],
scale: scale,
totalPlotClip: plotClip,
bitmap: bitmap,
vPatch: GraphicsOps.NewContextFromBitmap[bitmap]] ];
usedLevels: REF PACKED ARRAY CD.Level OF BOOL ← NIL;
abortPlot ← FALSE;
TerminalIO.WriteRope["Starting Versatec plot "];
TRUSTED {Process.SetPriority[Process.priorityBackground]};
viewer ← ViewerOps.FindViewer["Versatec Stripe"];
IF viewer #
NIL AND viewer.class.flavor # currentFlavor
THEN
{ViewerOps.DestroyViewer[viewer]; viewer ← NIL};
IF viewer=
NIL THEN
BEGIN
viewer ← ViewerOps.CreateViewer[flavor: currentFlavor,
info: [name: "Versatec Stripe", iconic: FALSE, openHeight: patchHeight+ViewerSpecs.menuHeight]];
END;
viewer.data ← ps;
dr.minimalSize ← 0;
dr.drawRect ← dr.saveRect ← NoteLevel;
dr.devicePrivate ← usedLevels;
dr.worldClip ← CDInline.universe;
dr.devicePrivate ← usedLevels ← NEW[PACKED ARRAY CD.Level OF BOOL ← ALL[FALSE]];
CDOps.DrawDesign[design, dr]; -- mark used levels
{ textureNo: CARDINAL ← 0;
FOR l:
CD.Level
IN CD.Level
DO -- assign textures to used levels
IF usedLevels[l] THEN { textures[l] ← Texture[textureNo]; textureNo ← textureNo+1}
ELSE textures[l] ← NIL;
ENDLOOP;
};
dr.drawRect ← dr.saveRect ← NoteRectangle;
dr.devicePrivate ← ps;
SELECT to
FROM
localFile =>
BEGIN
TerminalIO.WriteRope["onto """];
s ← FS.StreamOpen[plotFileOnLocalMachine, create];
TerminalIO.WriteRope[plotFileOnLocalMachine];
TerminalIO.WriteRope[""""];
s.SetLength[0];
latestPlotFile ← plotFileOnLocalMachine;
END;
requestFilename =>
BEGIN
r: Rope.ROPE ← CDExtras.AppendExt[TerminalIO.RequestRope["to file ? > "], "bits"];
TerminalIO.WriteRope["onto """];
s ← FS.StreamOpen[r, create];
TerminalIO.WriteRope[r];
TerminalIO.WriteRope[""""];
s.SetLength[0];
latestPlotFile ← r;
END;
remoteFile =>
BEGIN
--???--
TerminalIO.WriteRope["onto remote file not yet converted to Cedar 5"];
ERROR;
TerminalIO.WriteRope["onto remote file """];
s ← RemoteFiles.OpenAlpineForWrite[plotFileOnServer];
s.SetLength[0];
TerminalIO.WriteRope[plotFileOnServer];
TerminalIO.WriteRope[""""];
latestPlotFile ← plotFileOnServer;
END;
plotter =>
BEGIN
TerminalIO.WriteRope["onto plotter "];
[self: s] ← STPStreams.OpenSTP[host: plotter, fileName: plotFileOnPlotter, accessOptions: create];
latestPlotFile ← NIL;
END;
ENDCASE => ERROR;
TRUSTED {
s.PutChar[LOOPHOLE[scanLineWidth/256, CHARACTER]];
s.PutChar[
LOOPHOLE[scanLineWidth
MOD 256]];
send the length of a scan line to the Versatec
TerminalIO.WriteRope["\n.. found "];
TerminalIO.WriteInt[CDCallSpecific.CallForAll[design: design, whatElse: LookForSignalName, x: ps]];
TerminalIO.WriteRope[" top-level signal names "];
SendWhiteBar[s: s];
SendBlackBar[s: s];
FOR strip:
INT IN [0..strips)
DO
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[100./scale]+1,
y2: plotClip.y2];
FOR topLine:
INT ← 0, topLine+patchHeight
WHILE topLine<Real.Fix[scale*(clip.y2-clip.y1)]
DO
x, y, x1, y1, x2, y2: REAL;
dc: CD.DesignRect;
IF abortPlot
THEN GOTO AbortPlot;
[] ← ps.vPatch.SetPaintMode[opaque];
ps.vPatch.SetColor[Graphics.white];
ps.vPatch.DrawBox[[xmin: 0, ymin: 0, xmax: scanLineWidth, ymax: patchHeight]];
clear the bitmap
[] ← ps.vPatch.SetPaintMode[transparent];
ps.vPatch.SetColor[Graphics.black];
ps.context ← ps.vPatch.CopyContext[];
ps.context.Scale[sx: scale, sy: scale];
[dx: x, dy: y] ← Graphics.Map[sc: ps.vPatch, dc: ps.context,
sx: scanLineWidth/2, sy: topLine+patchHeight-1];
ps.context.Translate[tx: x-(clip.x2+clip.x1)/2, ty: y-clip.y2];
ps.context.ClipBox[box: [xmin: clip.x1, ymin: clip.y1, xmax: clip.x2, ymax: clip.y2]];
clip to the user-specified design rectangle
[dx: x1, dy: y1] ← Graphics.Map[sc: ps.vPatch, dc: ps.context, sx: -1, sy: -1];
[dx: x2, dy: y2] ← Graphics.Map[sc: ps.vPatch, dc: ps.context,
sx: scanLineWidth, sy: patchHeight];
dc ← CDInline.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
CDDialogue.ShowPosition[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 CornerStitching.ChangeRect[plane: ps.tes[l], rect: CDInline.universe, newvalue: NIL, checkOldvalue: FALSE];
ENDLOOP;
CDOps.DrawDesign[design, dr]; -- build tesselations of the relevant design rectangle
AnalyzeTesselations[ps];
[] ← ps.vPatch.SetPaintMode[opaque];
[] ← ps.text.EnumerateArea[rect: ps.totalPlotClip, perTile: PaintASignalName, data: ps];
ViewerOps.PaintViewer[viewer: viewer, hint: all, clearClient: topLine=0];
for debugging and to pacify the user
SendBitmap[s: s, bitmap: ps.bitmap];
s.Flush[];
TerminalIO.WriteRope["."];
ENDLOOP;
TerminalIO.WriteRope["*"];
SendBlackBar[s: s];
ENDLOOP;
SendWhiteBar[s: s];
s.Close[];
IF to # plotter AND spoolToPlotter THEN SpoolFileToPlotter[latestPlotFile];
TerminalIO.WriteRope["finished\n"];
EXITS
AbortPlot => {s ← AbortFile[s]; TerminalIO.WriteRope[" ** plot aborted ** "]; abortPlot ← FALSE};
END;
END;
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 CDInline.NonEmpty[r]
THEN
BEGIN
IF ps.tes[l]=NIL THEN ps.tes[l] ← CornerStitching.NewTesselation[];
ps.tes[l].ChangeRect[rect: r, newvalue: $covered, checkOldvalue: FALSE];
END;
END;
AnalyzeTesselations:
PROC [ ps: PlotStateRef ] =
BEGIN
FOR l:
CD.Level
IN CD.Level
DO
IF ps.tes[l]#
NIL THEN
BEGIN
ps.texture ← textures[l]^;
[] ← ps.tes[l].EnumerateArea[rect: ps.bandClip, perTile: ProcessTile, data: ps, backgroundValue: $none];
END;
ENDLOOP;
END;
ProcessTile:
PROCEDURE [tile: CornerStitching.TilePtr, data:
REF ANY]
RETURNS [
REF ANY]
-- CornerStitching.PerTileProc -- =
BEGIN -- only called on covered tiles
t: CornerStitching.TilePtr;
ps: PlotStateRef = NARROW[data];
tileValue: REF = tile.Value;
IF tileValue = $covered
THEN
BEGIN
r: CD.DesignRect = tile.Area;
GraphicsOps.DrawTexturedBox[self: ps.context, box: [xmin: r.x1, ymin: r.y1, xmax: r.x2, ymax: r.y2], texture: ps.texture];
END;
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.SetCP[x: tile.EastEdge, y: MAX[tile.SouthEdge, t.SouthEdge]];
ps.context.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.SetCP[x: MAX[tile.WestEdge, t.WestEdge], y: tile.NorthEdge];
ps.context.DrawTo[x: MIN[tile.EastEdge, t.EastEdge], y: tile.NorthEdge];
END;
ENDLOOP;
RETURN[data];
END;
SignalValueRef: TYPE = REF SignalValue;
SignalValue: TYPE = RECORD [s: Rope.ROPE];
LookForSignalName:
PROC [ design:
CD.Design, aptr:
CD.ApplicationPtr, x:
REF ]
RETURNS [done:
BOOL←
TRUE, 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 = CDInline.Intersection[ps.totalPlotClip, CDApplications.ApplicationRect[aptr]];
signal: REF = CDProperties.GetProp[from: aptr, prop: $SignalName];
IF signal #
NIL AND CDInline.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, checkOldvalue: FALSE];
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, checkOldvalue: FALSE];
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.CopyContext[];
signalValue: SignalValueRef = NARROW[tile.Value[]];
dx, dy, xmin, xmax, ymin, ymax: REAL;
r: CD.DesignRect = CDInline.Intersection[tile.Area, ps.totalPlotClip];
ctr: CD.DesignPosition = Center[r];
[dx: dx, dy: dy] ← Graphics.Map[sc: ps.context, 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;
Center:
PROC [ r:
CD.Rect ]
RETURNS [
CD.Position ] =
{RETURN[[x: (r.x1+r.x2)/2, y: (r.y1+r.y2)/2]]};
SendWhiteBar:
PROC [ s:
IO.
STREAM, lines:
CARDINAL ← 40 ] =
{SendBar[s, 0, lines]};
SendBlackBar:
PROC [ s:
IO.
STREAM, lines:
CARDINAL ← 20 ] =
{SendBar[s, 15, lines]};
SendBar:
PROC [ s:
IO.
STREAM, pattern: Nibble, lines:
CARDINAL ] =
BEGIN
scanLineNibbles: CARDINAL = scanLineWidth/4;
FOR j:
CARDINAL IN [0..lines)
DO
FOR i:
CARDINAL IN [0..(scanLineNibbles+15)/16)
DO
TRUSTED {s.PutChar[LOOPHOLE[CodeWord[repeats: MIN[16, scanLineNibbles-16*i], pattern: pattern]]]};
ENDLOOP;
ENDLOOP;
END;
SendBitmap:
PROC [ s:
IO.
STREAM, bitmap: GraphicsOps.BitmapRef ] =
BEGIN
csl: REF CodedScanLine = NEW[CodedScanLine ← [codeWords: NEW[CodeWords]]];
FOR i:
CARDINAL IN [0..bitmap.height)
DO -- send to the Versatec
TRUSTED {
EncodeScanLine[sl: LOOPHOLE[LOOPHOLE[bitmap.base, INT]+INT[bitmap.raster]*i, LONG POINTER TO ScanLine], bitWidth: bitmap.width, csl: csl];
s.UnsafePutBlock[[base: LOOPHOLE[csl.codeWords], startIndex: 0,
count: csl.codeWordCount]];
}
ENDLOOP;
END;
EncodeScanLine:
PROC [ sl:
LONG POINTER TO ScanLine, bitWidth:
CARDINAL, csl:
REF CodedScanLine ] =
TRUSTED BEGIN
size: CARDINAL ← 0;
scanLineInNibbles: LONG POINTER TO ScanLineInNibbles = LOOPHOLE[sl];
scanLineNibbles: CARDINAL = (bitWidth+3)/4;
i: CARDINAL ← 0;
WHILE i<scanLineNibbles
DO
pattern: Nibble = scanLineInNibbles[i];
j: CARDINAL ← i+1;
limit: CARDINAL ← MIN[scanLineNibbles, i+16];
WHILE j<limit
DO
IF scanLineInNibbles[j]#pattern THEN EXIT;
j ← j+1;
ENDLOOP;
csl.codeWords[size] ← [repeats: j-i, pattern: pattern];
size ← size+1;
i ← j;
ENDLOOP;
csl.codeWordCount ← size;
END;
Texture:
PROC [ index:
CARDINAL ]
RETURNS [
REF GraphicsOps.Texture ] =
BEGIN
t: REF GraphicsOps.Texture ← NEW[GraphicsOps.Texture];
FOR i: [0..16)
IN [0..16)
DO
Stipple: TYPE = ARRAY [0..4) OF [0..16);
row: [0..4) = i MOD 4;
t[i] ← (4096+256+16+1)*(
SELECT index
FROM
0 => Stipple[01H, 02H, 04H, 08H],
1 => Stipple[04H, 02H, 01H, 08H],
2 => Stipple[00H, 0AH, 00H, 0AH],
3 => Stipple[08H, 00H, 08H, 00H],
4 => Stipple[0AH, 00H, 00H, 00H],
5 => Stipple[08H, 00H, 02H, 00H],
6 => Stipple[00H, 01H, 00H, 04H],
7 => Stipple[04H, 04H, 00H, 00H],
8 => Stipple[00H, 00H, 00H, 03H],
ENDCASE => Stipple[0FH, 0FH, 0FH, 0FH])[row];
ENDLOOP;
RETURN[t];
END;
PaintVersatecStripe:
PROC [ self: ViewerClasses.Viewer, context: Graphics.Context,
whatChanged:
REF ANY, clear:
BOOL ]
-- ViewerClasses.PaintProc -- =
BEGIN
ps: PlotStateRef = NARROW[self.data];
x: INT = MAX[INT[0], (ps.bitmap.width-self.cw)/2];
y: INT = MAX[INT[0], (ps.bitmap.height-self.ch)/2];
context.SetCP[self.cw/2, self.ch/2];
GraphicsOps.DrawBitmap[self: context, bitmap: ps.bitmap,
w: MIN[self.cw, ps.bitmap.width], h: MIN[self.ch, ps.bitmap.height],
x: x, y: y, xorigin: ps.bitmap.width/2, yorigin: ps.bitmap.height/2];
END;
AbortProc: Menus.MenuProc = {abortPlot ←
TRUE};
AbortFile:
PROC [s:
IO.
STREAM ]
RETURNS [
IO.
STREAM ] =
{IF s#NIL THEN s.Close[abort: TRUE]; RETURN[NIL]};
SpoolToPlotter:
ENTRY PROC = {SpoolFileToPlotter[latestPlotFile]};
SpoolFileToPlotter:
PROC [name: Rope.
ROPE] =
BEGIN
IF name #
NIL THEN {
TerminalIO.WriteRope["spool file """];
TerminalIO.WriteRope[name];
TerminalIO.WriteRope[""".."];
FS.Copy[from: name, to: plotFileToSpool !
FS.Error =>
IF error.group # bug
THEN {
TerminalIO.WriteRope[" not done: "];
TerminalIO.WriteRope[error.explanation];
TerminalIO.WriteLn[];
CONTINUE;
}
];
TerminalIO.WriteRope[" spool finished\n"];
};
END;
Module START code...
END. -- of VersatecImpl