<> <> <> <> <> <> <> <> <> <> <> <> 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], <> 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]plot.bits"; plotFileOnPlotter: Rope.ROPE _ "plot.bits"; plotFileOnLocalMachine: Rope.ROPE _ "///chipndale/tem/plot.bits"; latestPlotFile: Rope.ROPE _ NIL; spoolToPlotter: BOOL _ TRUE; abortPlot: BOOL _ FALSE; designIfAbort: CD.Design _ NIL; PlotDesign: INTERNAL PROC [ c: CDSequencer.Command, to: {localFile, plotter, requestFilename}] = BEGIN s: IO.STREAM _ NIL; 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 BOOL _ NIL; 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 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 _ 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]]; <> }; <<>> 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 _ 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]]; <> 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]; <> 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; <> 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; 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 = 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] = <> 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]]; <> 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 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; <<>> <> <<>> Init[]; END. -- of VersatecImpl