<> <> <> <> <> <> <> <> <> <> <> DIRECTORY Atom, CD, CDApplications, CDCallSpecific, CDViewer, CDExtras, CDInline, CDOps, CDProperties, CDSequencer, ConvertUnsafe, CornerStitching, FS, Graphics, GraphicsOps, Menus, IO, Process, Real, Rope, STPStreams, TerminalIO, VFonts, ViewerClasses, ViewerOps, ViewerSpecs; 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], <> 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"]; <> }; 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]plot.bits"; plotFileOnPlotter: Rope.ROPE _ "plot.bits"; plotFileOnLocalMachine: Rope.ROPE _ "plot.bits"; plotFileOnServer: Rope.ROPE _ "[Luther.alpine]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 <>> <> <> <> <> 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)]; <
> patchHeight: CARDINAL = stippleRepeat*((MIN[maxPatchHeight, CARDINAL[Real.Fix[50*CD.lambda*scale]]]+stippleRepeat-1)/stippleRepeat); <> 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; <> <> <> <> <> <> 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]]; <> }; <<>> 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> [] _ 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]]; <> [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]]; <> 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, 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]; <> 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; <> FOR t _ tile.NEastNeighbour, t.WSouthNeighbour WHILE tile.SouthEdge> 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> 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 <> 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]]; <> 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 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; <<>> <> <<>> Init[]; END. -- of VersatecImpl