VersatecImpl:
CEDAR
MONITOR
IMPORTS Atom, CD, CDApplications, CDCallSpecific, CDViewer, 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];
CDSequencer.ImplementCommand[a~$VersatecBWPlot, 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
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 CornerStitching.ChangeRect[plane: ps.tes[l], rect: CDInline.universe, newValue: NIL];
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];
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]
=
-- 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;
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.ARectO[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];
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] =
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];
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