VersatecImpl.mesa
Copyright © 1983, 1984 by Xerox Corporation. All rights reserved.
A package to output a ChipnDale design for the Versatec plotter in Oliver format. That format is defined thus:
Bytes 0, 1 (high-order first):
The number of bits per Versatec scan line (probably must be divisible by 4).
Byte j:
Two four-bit nibbles. The high-order nibble tells how many times to repeat the low-order nibble in the output bit stream. A data byte may not carry from one scan line to the next.
N. B. This is not a very economical format. You should be able at least to duplicate previous scan lines.
written by E. McCreight, August 1, 1983 2:00 PM
Last Edited by: Mccreight, November 23, 1983 4:58 pm
Last Edited by: Jacobi, October 27, 1983 12:36 pm
Last Edited by: Jacobi, November 5, 1984 2:27:41 pm PST
DIRECTORY
Atom,
CD,
CDApplications,
CDBasics,
CDCallSpecific,
CDViewer,
CDEvents,
CDIO,
CDMenus,
CDOps,
CDProperties,
CDSequencer,
CornerStitching,
FS,
Graphics,
GraphicsOps,
Menus,
IO,
Process,
Real,
Rope,
STPStreams,
TerminalIO,
VFonts,
ViewerClasses,
ViewerOps,
ViewerSpecs;
VersatecImpl: CEDAR MONITOR
IMPORTS Atom, CD, CDApplications, CDBasics, CDCallSpecific, CDEvents, CDIO, CDMenus, CDViewer, 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]];
CDEvents.RegisterEventProc[$Abort, AbortEvent];
ViewerOps.RegisterViewerClass[flavor: currentFlavor,
class: NEW[ViewerClasses.ViewerClassRec ← [paint: PaintVersatecStripe, menu: menu]]];
CDSequencer.ImplementCommand[a~$VPlotLocal, p~PlotDesignToLocalFile];
CDSequencer.ImplementCommand[a~$VPlotPlotter, p~PlotDesignToPlotter];
CDSequencer.ImplementCommand[a~$VPlot, p~PlotDesignToAny];
CDSequencer.ImplementCommand[a~$VersatecBWPlot, p~PlotDesignToAny];
CDMenus.CreateEntry[menu~$RectProgramMenu, entry~"BW Versatec Plot", key~$VersatecBWPlot];
signalFont ← VFonts.GraphicsFont[VFonts.EstablishFont[family: "Helvetica", size: 18, bold: TRUE]];
TerminalIO.WriteRope["ChipNDale BW Versatec plotter loaded\n"];
TerminalIO.WriteRope[" ..use the command for additional programs using a rectangle for plotting..\n"];
};
PlotDesignToAny: ENTRY PROC [ c: CDSequencer.Command ] =
BEGIN ENABLE UNWIND => NULL;
n: CARDINAL ← TerminalIO.RequestSelection[
label: "Versatec Plot",
choice: LIST[" plotter", " local", " read name"]
];
SELECT n FROM
1 => PlotDesign[c: c, to: plotter];
2 => PlotDesign[c: c, to: localFile];
3 => PlotDesign[c: c, to: requestFilename];
ENDCASE => TerminalIO.WriteRope["skipped\n"];
END;
PlotDesignToLocalFile: ENTRY PROC [ c: CDSequencer.Command ] =
BEGIN ENABLE UNWIND => NULL;
PlotDesign[c: c, to: localFile];
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 ← "///chipndale/tem/plot.bits";
latestPlotFile: Rope.ROPENIL;
spoolToPlotter: BOOLTRUE;
abortPlot: BOOLFALSE;
designIfAbort: CD.Design ← NIL;
PlotDesign: INTERNAL PROC [ c: CDSequencer.Command, to: {localFile, plotter, requestFilename}] =
BEGIN
s: IO.STREAMNIL;
strips: [1..100] ← TerminalIO.RequestInt["How many vertical strips in the plot? "];
BEGIN ENABLE { -- for ERRORs
UNWIND => {s ← AbortFile[s]; TerminalIO.WriteRope[" ** plot aborted ** "]};
};
design: CD.Design = c.design;
plotClip: CD.DesignRect = CDBasics.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 BOOLNIL;
abortPlot ← FALSE;
designIfAbort ← design;
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 ← CDBasics.universe;
dr.devicePrivate ← usedLevels ← NEW[PACKED ARRAY CD.Level OF BOOLALL[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 ← CDIO.MakeName[TerminalIO.RequestRope["to file ? > "], "bits"];
TerminalIO.WriteRope["onto """];
s ← FS.StreamOpen[r, create];
TerminalIO.WriteRope[r];
TerminalIO.WriteRope[""""];
s.SetLength[0];
latestPlotFile ← r;
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 ← 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
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: CDBasics.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];
designIfAbort ← NIL;
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 CDBasics.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: BOOLTRUE, removeMe: BOOLFALSE, include: CD.ApplicationList←NIL,
repaintMe: BOOLFALSE, repaintInclude: BOOLFALSE] -- 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.ARectO[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] =
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 = CDBasics.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: CARDINALMIN[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};
AbortEvent: CDEvents.EventProc = {
IF designIfAbort#NIL AND (design=NIL OR design=designIfAbort) THEN {
abortPlot ← TRUE; designIfAbort ← NIL
}
};
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...
Init[];
END. -- of VersatecImpl