DIRECTORY BasicTime USING [GMT], Imager USING [Box, ClipRectangle, ConstantColor, Context, DoSave, MaskBox, MaskStroke, MaskVector, PathProc, Rectangle, Scale2T, SetColor, SetFont, SetStrokeEnd, SetStrokeJoint, SetStrokeWidth, SetXY, ShowRope, TranslateT, VEC], ImagerBackdoor USING [GetBounds, GetCP], ImagerFont USING [Extents, Find, Font, FontBoundingBox, RopeBoundingBox, Scale], IO USING [PutFR, real, rope, time], Plot USING [Curves, PlotSpec, PlotSpecRec, RopeSequence, Vector], PlotOps USING [BackgroundType, color, ColorMode, Handle, MaxStep, LineState, LineStateRec, LineStep, NColors, OutputType, StateSequence], Real USING [FixI, RoundI, RoundLI], RealFns USING [Log, Power, SqRt], Rope USING [Find, IsEmpty, ROPE, Substr], VFonts USING [EstablishFont], ViewerClasses USING [PaintRectangle]; PlotViewerPaint: CEDAR PROGRAM IMPORTS BasicTime, Imager, ImagerBackdoor, ImagerFont, IO, PlotOps, Real, RealFns, Rope, VFonts EXPORTS PlotOps = { OPEN Plot, PlotOps; TextHeightFudge: REAL = 4.0; TextWidthFudge: REAL = 4.0; minLineWidth: REAL = 1.0; t12b: ImagerFont.Font = VFonts.EstablishFont["TimesRoman", 12, TRUE]; -- bold t12bp: ImagerFont.Font = ImagerFont.Scale[ ImagerFont.Find["Xerox/PressFonts/TimesRoman-mrr"], 13.0]; h8: ImagerFont.Font = VFonts.EstablishFont["Helvetica", 8]; h8p: ImagerFont.Font = ImagerFont.Scale[ ImagerFont.Find["Xerox/PressFonts/Helvetica-mrr"], 9.0]; textFont: PUBLIC ARRAY FontType OF ARRAY OutputType OF ImagerFont.Font = [[t12b, t12bp, t12bp], [h8, h8p, h8p]]; fontHeight: PUBLIC ARRAY FontType OF ARRAY OutputType OF REAL; FontType: TYPE = {title, normal}; DrawMe: PUBLIC PROC [context: Imager.Context, handle: Handle, whatChanged: REF ANY, output: OutputType] = { BoundsValid: PROC [box: Imager.Box] RETURNS [BOOL] = INLINE { RETURN[box.xmax > box.xmin AND box.ymax > box.ymin]; }; -- BoundsValid rect: Imager.Rectangle _ IF output = interpress THEN [0.0, -518.16, 670.56, 518.16] ELSE ImagerBackdoor.GetBounds[context]; box: Imager.Box _ [rect.x, rect.y, rect.x + rect.w, rect.y + rect.h]; foreground: Imager.ConstantColor _ IF output # screen THEN color[15] ELSE IF handle.background = black OR handle.background = darkGray THEN color[0] ELSE color[15]; IF whatChanged = NIL OR ISTYPE[whatChanged, ViewerClasses.PaintRectangle] THEN { background: Imager.ConstantColor _ IF output # screen THEN color[0] ELSE SELECT handle.background FROM gray => color[13], darkGray => color[14], black => color[15], ENDCASE => color[0]; width: REAL _ rect.w; height: REAL _ rect.h; marginRatio: REAL = 0.05; innerRatio: REAL = 0.9; marginX: REAL _ width*marginRatio; marginY: REAL _ height*marginRatio; maxX: REAL _ box.xmax - marginX; maxY: REAL _ box.ymax - marginY; minX: REAL _ box.xmin + marginX; minY: REAL _ box.ymin + marginY; innerWidth: REAL _ width * innerRatio; innerHeight: REAL _ height * innerRatio; tenthHeight: REAL _ innerHeight/10.0; textLineHeight: REAL _ fontHeight[normal][output] + TextHeightFudge; windowTooLow: BOOL _ tenthHeight < textLineHeight; legendTop: REAL _ IF windowTooLow THEN minY ELSE minY + tenthHeight*1.8; axesTop: REAL _ IF windowTooLow THEN maxY ELSE maxY - tenthHeight; titleBox: Imager.Box _ [minX, axesTop, maxX, maxY]; axesBox: Imager.Box _ [minX, legendTop, maxX, axesTop]; legendBox: Imager.Box _ [minX, minY, maxX, legendTop]; IF output = screen THEN { -- clear the screen context.SetColor[background]; context.MaskBox[box]; }; context.SetColor[foreground]; IF NOT windowTooLow THEN { excl: INT _ handle.plotSpec.file.Find["!"]; pattern: Rope.ROPE = IF excl < 0 THEN handle.plotSpec.file ELSE handle.plotSpec.file.Substr[0, excl]; footNote: Rope.ROPE _ IO.PutFR["File: %g; time: %g.", IO.rope[pattern], IO.time[handle.plotSpec.time]]; DrawTitle[context, titleBox, handle.plotSpec.title, output]; DrawLegendFootnote[context, legendBox, handle.plotSpec.legendEntries, footNote, output, handle.colorMode]; }; IF BoundsValid[handle.plotSpec.bounds] THEN { c: Curves _ NIL; [handle.curvesBox, handle.realBounds] _ DrawAxes[context, axesBox, handle.plotSpec.bounds, output]; context.ClipRectangle[ [handle.curvesBox.xmin, handle.curvesBox.ymin, handle.curvesBox.xmax - handle.curvesBox.xmin, handle.curvesBox.ymax - handle.curvesBox.ymin]]; FOR l: Curves _ handle.curves, l.rest UNTIL l = NIL DO c _ CONS[l.first, c]; ENDLOOP; DrawCurves[context, handle, c, output]; }; } ELSE IF ISTYPE[whatChanged, Curves] THEN { OPEN handle; cv: Curves _ NARROW[whatChanged]; IF cv # NIL AND BoundsValid[handle.realBounds] THEN { m0, m1: Vector; m1 _ cv.first; IF m1 = NIL OR cv.rest = NIL THEN RETURN; m0 _ cv.rest.first; IF m0 = NIL THEN RETURN; IF m0.size = m1.size THEN { tMin: REAL = realBounds.xmin; tFactor: REAL = (curvesBox.xmax - curvesBox.xmin + 1)/(realBounds.xmax - tMin); vMin: REAL = realBounds.ymin; vFactor: REAL = (curvesBox.ymax - curvesBox.ymin + 1)/(realBounds.ymax - vMin); t0: REAL = (m0[0] - tMin)*tFactor; t1: REAL = (m1[0] - tMin)*tFactor; context.ClipRectangle[ [handle.curvesBox.xmin, handle.curvesBox.ymin, handle.curvesBox.xmax - handle.curvesBox.xmin, handle.curvesBox.ymax - handle.curvesBox.ymin]]; context.TranslateT[[curvesBox.xmin, curvesBox.ymin]]; context.SetStrokeEnd[round]; context.SetStrokeWidth[minLineWidth]; IF handle.colorMode = bw THEN { context.SetColor[foreground]; FOR index: CARDINAL IN [1..m0.size) DO ipath: CARDINAL _ index - 1; DrawLineSeg[context, [t0, (m0[index]-vMin)*vFactor], [t1, (m1[index]-vMin)*vFactor], ipath MOD 12, handle.lineStates[ipath]]; ENDLOOP; } ELSE FOR index: CARDINAL IN [1..m0.size) DO ipath: CARDINAL _ index - 1; context.SetColor[color[(ipath MOD 12) + 1]]; context.MaskVector[ [t0, (m0[index]-vMin)*vFactor], [t1, (m1[index]-vMin)*vFactor]]; ENDLOOP; }; }; }; }; -- DrawMe DrawTitle: PROC [context: Imager.Context, box: Imager.Box, title: Rope.ROPE, output: OutputType] = { xPos: REAL _ box.xmin + (box.xmax - box.xmin) / 2.0; boxH: REAL _ box.ymax - box.ymin - fontHeight[normal][output]; IF title.IsEmpty[] THEN RETURN; IF boxH >= fontHeight[title][output] THEN [] _ MyDrawText[context, title, xPos, box.ymax, center, top, title, output] ELSE IF boxH >= fontHeight[normal][output] THEN [] _ MyDrawText[context, title, xPos, box.ymax, center, top, normal, output]; }; -- DrawTitle DrawAxes: PROC [context: Imager.Context, box, bounds: Imager.Box, output: OutputType] RETURNS [innerBox, realBounds: Imager.Box] = { Border: PROC = { context.SetStrokeEnd[square]; context.SetStrokeJoint[round]; context.SetStrokeWidth[minLineWidth*2.0]; IF output = press THEN { -- Press doesn't support MaskStroke. context.MaskVector[[innerBox.xmin, innerBox.ymin], [innerBox.xmin, innerBox.ymax]]; context.MaskVector[[innerBox.xmin, innerBox.ymax], [innerBox.xmax, innerBox.ymax]]; context.MaskVector[[innerBox.xmax, innerBox.ymax], [innerBox.xmax, innerBox.ymin]]; context.MaskVector[[innerBox.xmax, innerBox.ymin], [innerBox.xmin, innerBox.ymin]]; } ELSE { borderPath: Imager.PathProc = { moveTo[[innerBox.xmin, innerBox.ymin]]; lineTo[[innerBox.xmin, innerBox.ymax]]; lineTo[[innerBox.xmax, innerBox.ymax]]; lineTo[[innerBox.xmax, innerBox.ymin]]; }; context.MaskStroke[path: borderPath, closed: TRUE]; }; }; -- Border HorizontalAxisLabels: PROC[context: Imager.Context, box: Imager.Box, min, max, scale, step: REAL] = { tickLen: REAL _ (box.ymax - box.ymin)/50.0; tickCount: CARDINAL = Real.RoundI[(max - min)/step]; textTop: REAL _ box.ymin - TextHeightFudge; tick: REAL; FOR i: CARDINAL IN [0..tickCount] DO tick _ step*scale*i; [] _ MyDrawText[context, IO.PutFR["%g", IO.real[step*i + min]], box.xmin + tick, textTop, center, top, normal, output]; IF i # 0 AND i # tickCount THEN { xPos: REAL = box.xmin + tick; yStart: REAL _ box.ymin; yEnd: REAL _ box.ymin + tickLen; Tick: Imager.PathProc = {moveTo[[xPos, yStart]]; lineTo[[xPos, yEnd]]}; proc: PROC = {context.MaskStroke[path: Tick, closed: FALSE]}; IF output = press THEN context.MaskVector[[xPos, yStart], [xPos, yEnd]] ELSE context.DoSave[proc]; yStart _ box.ymax; yEnd _ box.ymax - tickLen; IF output = press THEN context.MaskVector[[xPos, yStart], [xPos, yEnd]] ELSE context.DoSave[proc]; }; ENDLOOP; }; -- HorizontalAxisLabels VerticalAxisLabels: PROC[context: Imager.Context, box: Imager.Box, min, max, scale, step: REAL] = { tickLen: REAL _ (box.ymax - box.ymin)/50.0; tickCount: CARDINAL = Real.RoundI[(max - min)/step]; textRight: REAL _ box.xmin - TextWidthFudge; tick: REAL; FOR i: CARDINAL IN [0..tickCount] DO tick _ step*scale*i; [] _ MyDrawText[context, IO.PutFR["%g", IO.real[step*i + min]], textRight, box.ymin + tick, right, center, normal, output]; IF i # 0 AND i # tickCount THEN { yPos: REAL = box.ymin + tick; xStart: REAL _ box.xmin; xEnd: REAL _ box.xmin + tickLen; Tick: Imager.PathProc = {moveTo[[xStart, yPos]]; lineTo[[xEnd, yPos]]}; proc: PROC = {context.MaskStroke[path: Tick, closed: FALSE]}; IF output = press THEN context.MaskVector[[xStart, yPos], [xEnd, yPos]] ELSE context.DoSave[proc]; xStart _ box.xmax; xEnd _ box.xmax - tickLen; IF output = press THEN context.MaskVector[[xStart, yPos], [xEnd, yPos]] ELSE context.DoSave[proc]; }; ENDLOOP; }; -- VerticalAxisLabels xSize: REAL = box.xmax-box.xmin; ySize: REAL = box.ymax-box.ymin; xStep, yStep: REAL; -- between ticks xScale, yScale: REAL; nLabelsX, nLabelsY: CARDINAL; hLabelWidth, hLabelHeight, vLabelWidth, vLabelHeight, tLabelWidth, tLabelHeight: REAL; inBoxW, inBoxH: REAL; [tLabelWidth, tLabelHeight] _ GetRopeSize[IO.PutFR["%g", IO.real[bounds.xmin]], normal, output]; [hLabelWidth, hLabelHeight] _ GetRopeSize[IO.PutFR["%g", IO.real[bounds.xmax]], normal, output]; hLabelWidth _ MAX[tLabelWidth, hLabelWidth]; hLabelHeight _ MAX[tLabelHeight, hLabelHeight]; [tLabelWidth, tLabelHeight] _ GetRopeSize[IO.PutFR["%g", IO.real[bounds.ymin]], normal, output]; [vLabelWidth, vLabelHeight] _ GetRopeSize[IO.PutFR["%g", IO.real[bounds.ymax]], normal, output]; vLabelWidth _ MAX[tLabelWidth, vLabelWidth]; vLabelHeight _ MAX[tLabelHeight, vLabelHeight]; innerBox _ [ xmin: box.xmin + vLabelWidth + TextWidthFudge, ymin: box.ymin + hLabelHeight + TextHeightFudge*2, xmax: box.xmax, ymax: box.ymax]; IF innerBox.xmin > innerBox.xmax THEN innerBox.xmin _ box.xmin; IF innerBox.ymin > innerBox.ymax THEN innerBox.ymin _ box.ymin; inBoxW _ innerBox.xmax - innerBox.xmin; inBoxH _ innerBox.ymax - innerBox.ymin; context.DoSave[Border]; nLabelsX _ MAX[1, MIN[10, Real.RoundI[inBoxW/hLabelWidth*5/8]]]; nLabelsY _ MAX[1, MIN[10, Real.RoundI[inBoxH/vLabelHeight*5/8]]]; [realBounds.xmin, realBounds.xmax, xStep, xScale] _ ScaleAxis[ bounds.xmin, bounds.xmax, inBoxW, nLabelsX]; [realBounds.ymin, realBounds.ymax, yStep, yScale] _ ScaleAxis[ bounds.ymin, bounds.ymax, inBoxH, nLabelsY]; context.SetStrokeEnd[butt]; context.SetStrokeWidth[minLineWidth]; HorizontalAxisLabels[context, innerBox, realBounds.xmin, realBounds.xmax, xScale, xStep]; VerticalAxisLabels[context, innerBox, realBounds.ymin, realBounds.ymax, yScale, yStep]; }; -- DrawAxes GetRopeSize: PROC [text: Rope.ROPE, fontType: FontType _ normal, output: OutputType _ screen] RETURNS [dx, dy: REAL] = { font: ImagerFont.Font_ textFont[fontType][output]; extents: ImagerFont.Extents _ ImagerFont.RopeBoundingBox[font, text]; dx _ extents.rightExtent - extents.leftExtent; dy _ extents.descent + extents.ascent; }; -- GetRopeSize ScaleAxis: PROC [minDataValue, maxDataValue, innerBoxSize: REAL, nLabels: CARDINAL] RETURNS [min, max, step, scale: REAL] = { step _ FindStepSize[maxDataValue - minDataValue, nLabels]; min _ AlignEnd[minDataValue, step, FALSE]; max _ AlignEnd[maxDataValue, step, TRUE]; IF Almost[min, max] THEN {max _ max + 50.0; min _ min - 50.0}; scale _ innerBoxSize/(max - min); }; -- ScaleAxis FindStepSize: PROC [range: REAL, nSteps: CARDINAL] RETURNS[step: REAL] = { logRange: REAL; mantissa, minStep: REAL; characteristic: INTEGER; steps: ARRAY [0..6) OF REAL = [0.2, 0.5, 1.0, 2.0, 5.0, 10.0]; IF Almost[range, 0.0] THEN range _ 100.0; logRange _ RealFns.Log[10.0, range]; characteristic _ Real.FixI[logRange]; mantissa _ logRange - characteristic; IF logRange < 0.0 THEN { characteristic _ characteristic - 1; mantissa _ mantissa + 1.0}; minStep _ RealFns.Power[10.0, mantissa]/nSteps; FOR i: CARDINAL IN [0..5) DO step _ steps[i]; IF step > minStep OR Almost[step, minStep] THEN EXIT ENDLOOP; IF characteristic >= 0 THEN THROUGH [1..characteristic] DO step _ step*10.0 ENDLOOP ELSE THROUGH [1..-characteristic] DO step _ step/10.0 ENDLOOP; step _ MAX[1.0, step]; }; -- FindStepSize AlignEnd: PROC [e, step: REAL, roundUp: BOOL] RETURNS [ae: REAL] = { absE: REAL = ABS[e]; nSteps: INTEGER; xend: REAL; IF e = 0.0 THEN RETURN[0.0]; IF e < 0.0 THEN roundUp _ ~roundUp; nSteps _ Real.RoundLI[absE/step - 0.5]; xend _ step*nSteps; IF Almost[nSteps, absE/step] THEN ae _ xend ELSE IF roundUp THEN ae _ IF xend >= absE THEN xend ELSE step*(nSteps + 1) ELSE ae _ IF xend <= absE THEN xend ELSE step*(nSteps - 1); IF e < 0.0 THEN ae _ -ae; }; -- AlignEnd Almost: PROC [p, q: REAL] RETURNS [a: BOOL _ TRUE] = { max: REAL _ MAX[ABS[p], ABS[q]]; IF max # 0.0 THEN a _ (ABS[p - q]/max) < 0.00001; }; -- Almost MyDrawText: PROC[context: Imager.Context, text: Rope.ROPE, x0, y0: REAL, xJustification: {left, center, right} _ left, yJustification: {top, center, bottom} _ bottom, fontType: FontType _ normal, output: OutputType _ screen, xScale, yScale: REAL _ 1.0] RETURNS [Imager.VEC] = { proc: PROC = { context.TranslateT[[x0, y0]]; context.SetXY[[xmin, ymin]]; context.SetFont[font]; context.Scale2T[[xScale, yScale]]; context.ShowRope[text]; }; -- proc extents: ImagerFont.Extents; xmin, ymin, width, height: REAL; font: ImagerFont.Font = textFont[fontType][output]; IF text.IsEmpty[] THEN RETURN[[x0, y0]]; extents _ ImagerFont.RopeBoundingBox[font: font, rope: text]; extents _ [extents.leftExtent*xScale, extents.rightExtent*xScale, extents.descent*yScale, extents.ascent*yScale]; width _ extents.rightExtent - extents.leftExtent; height _ extents.descent + extents.ascent; xmin_ SELECT xJustification FROM right => - width - extents.leftExtent, center => - width/2.0 - extents.leftExtent, ENDCASE => - extents.leftExtent; ymin_ SELECT yJustification FROM top => - height + extents.descent, center => - height/2.0 + extents.descent, ENDCASE => extents.descent; context.DoSave[proc]; RETURN[IF output = interpress THEN [x0 + width, y0 + height] ELSE ImagerBackdoor.GetCP[context]]; }; -- MyDrawText DrawCurves: PROC [context: Imager.Context, handle: Handle, curves: Curves, output: OutputType] = { box: Imager.Box _ handle.curvesBox; bounds: Imager.Box _ handle.realBounds; colorMode: ColorMode _ handle.colorMode; IF curves # NIL THEN { nCurvesMax: CARDINAL = curves.first.size - 1; tFactor: REAL _ (box.xmax - box.xmin)/(bounds.xmax - bounds.xmin); vFactor: REAL _ (box.ymax - box.ymin)/(bounds.ymax - bounds.ymin); drawThem: PROC = { context.TranslateT[[box.xmin, box.ymin]]; context.SetStrokeWidth[minLineWidth]; context.SetStrokeJoint[mitered]; IF colorMode = bw THEN { context.SetStrokeEnd[butt]; FOR i: CARDINAL IN [1..nCurvesMax] DO firstPoint: BOOL _ TRUE; lastVec, newVec: Imager.VEC; index: CARDINAL _ i - 1; handle.lineStates[index]^ _ []; -- cf. AddVector. FOR graph: Curves _ curves, graph.rest UNTIL graph = NIL DO newVec _ [ (graph.first[0] - bounds.xmin)*tFactor, (graph.first[i] - bounds.ymin)*vFactor]; IF firstPoint THEN firstPoint _ FALSE ELSE DrawLineSeg[context, lastVec, newVec, index MOD 12, handle.lineStates[index]]; lastVec _ newVec; ENDLOOP; ENDLOOP; } ELSE { context.SetStrokeEnd[round]; FOR i: CARDINAL IN [1..nCurvesMax] DO context.SetColor[color[((i-1) MOD 12)+1]]; IF output = press THEN { normal: BOOL _ FALSE; lastVec: Imager.VEC; FOR graph: Curves _ curves, graph.rest UNTIL graph = NIL DO t: REAL _ (graph.first[0] - bounds.xmin)*tFactor; v: REAL _ (graph.first[i] - bounds.ymin)*vFactor; IF normal THEN context.MaskVector[lastVec, [t, v]] ELSE normal _ TRUE; lastVec _ [t, v]; ENDLOOP; } ELSE { oneCurve: Imager.PathProc = { start: BOOL _ TRUE; FOR graph: Curves _ curves, graph.rest UNTIL graph = NIL DO t: REAL _ (graph.first[0] - bounds.xmin)*tFactor; v: REAL _ (graph.first[i] - bounds.ymin)*vFactor; IF start THEN moveTo[[t, v]] ELSE lineTo[[t, v]]; start _ FALSE; ENDLOOP; }; -- oneCurve context.MaskStroke[path: oneCurve, closed: FALSE]; }; ENDLOOP; }; }; -- drawThem IF nCurvesMax > 0 THEN context.DoSave[drawThem]; }; }; -- DrawCurves DrawLegendFootnote: PROC [context: Imager.Context, box: Imager.Box, legendEntries: REF RopeSequence, footNote: Rope.ROPE, output: OutputType, colorMode: ColorMode] = { nEntries: CARDINAL _ legendEntries.size; MaxEntriesPerColumn: CARDINAL = 5; xmin, xmax: REAL _ 0; yIncPerRow: REAL _ MAX[-fontHeight[normal][output], (box.ymin - box.ymax) / MaxEntriesPerColumn]; -- note: they are negative. yScale: REAL = -yIncPerRow / fontHeight[normal][output]; colorSampleLineLength: REAL = 34.0; sampleLineLength: ARRAY[0..12) OF REAL = [ 34.0, 34.0, 34.0, 34.0, 34.0, 34.0, 34.0, 34.0, 31.0, 31.0, 34.0, 34.0]; state: LineState _ NEW[LineStateRec]; proc: PROC = { context.TranslateT[[box.xmin, box.ymax]]; IF yScale >= 0.99 THEN [] _ MyDrawText[context, footNote, 0, -(box.ymax-box.ymin), left, top, normal, output]; FOR i: CARDINAL IN [0..nEntries) DO row: CARDINAL _ i MOD MaxEntriesPerColumn; col: CARDINAL _ i / MaxEntriesPerColumn; index: CARDINAL _ i MOD 12; textStartX, textStartY, lineY, newX: REAL; IF row = 0 AND col > 0 THEN { xmin _ xmax + TextWidthFudge; IF xmin >= box.xmax THEN EXIT; }; textStartX _ xmin + 36.0; textStartY _ row*yIncPerRow; lineY _ textStartY + (yIncPerRow/2.0); context.SetStrokeWidth[minLineWidth]; context.SetStrokeEnd[butt]; IF colorMode = bw THEN { state^ _ []; DrawLineSeg[context, [xmin, lineY], [xmin+sampleLineLength[index], lineY], index, state]; } ELSE { context.SetColor[color[index+1]]; context.MaskVector[[xmin, lineY], [xmin+colorSampleLineLength, lineY]]; }; [[newX, ]] _ MyDrawText [ context, legendEntries[i], textStartX, textStartY, left, top, normal, output, 1.0, yScale]; IF newX > xmax THEN xmax _ newX; ENDLOOP; }; -- proc context.DoSave[proc]; }; -- DrawLegendFootnote LineShape: TYPE = {solid, dot, dash, longDash}; StepSize: ARRAY LineShape[dot..longDash] OF REAL = [3.0, 6.0, 9.0]; LineLimit: ARRAY LineShape[dot..longDash] OF REAL = [1.0, 4.0, 7.0]; -- beyond which is space. LineStyle: TYPE = NColors[0..12); shape: ARRAY LineStyle OF ARRAY LineStep OF LineShape = [ [solid, solid, solid, solid, solid, solid], -- solid, useless [dot, dot, dot, dot, dot, dot], -- dot [dash, dash, dash, dash, dash, dash], -- dash [longDash, longDash, longDash, longDash, longDash, longDash], -- longDash [dot, dash, dot, dash, dot, dash], -- dot-dash [dot, longDash, dot, longDash, dot, longDash], -- dot-longDash [dot, dot, dash, dot, dot, dash], -- dot-dot-dash [dot, dot, longDash, dot, dot, longDash], -- dot-dot-longDash [dot, dash, dash, dot, dash, dash], -- dot-dash-dash [dot, longDash, longDash, dot, longDash, longDash], -- dot-longDash-longDash [dash, longDash, dash, longDash, dash, longDash], -- dash-longDash [dot, dash, longDash, dot, dash, longDash] -- dot-dash-longDash ]; DrawLineSeg: PROC[context: Imager.Context, v0, v1: Imager.VEC _ [0.0, 0.0], style: LineStyle _ 0, state: LineState _ NIL] = { IF Almost[v0.x, v1.x] AND Almost[v0.y, v1.y] THEN RETURN; IF state = NIL THEN state _ NEW[LineStateRec _ []]; IF style = 0 THEN context.MaskVector[v0, v1] ELSE { -- Note that normally state.step and state.progress will be updated. dx: REAL = v1.x - v0.x; dy: REAL = v1.y - v0.y; lengthToGo: REAL _ RealFns.SqRt[dx*dx + dy*dy]; cosine: REAL _ dx/lengthToGo; sine: REAL _ dy/lengthToGo; newVec, oldVec: Imager.VEC _ v0; stepSize, lineLimit, increment: REAL; drawIt: BOOL; UNTIL Almost[lengthToGo, 0.0] DO stepSize_ StepSize[shape[style][state.step]]; lineLimit_ LineLimit[shape[style][state.step]]; IF state.progress < lineLimit THEN { drawIt _ TRUE; increment _ MIN[lengthToGo, lineLimit - state.progress]; } ELSE { drawIt _ FALSE; increment _ MIN[lengthToGo, stepSize - state.progress]; }; lengthToGo _ lengthToGo - increment; state.progress _ state.progress + increment; newVec _ [oldVec.x + cosine*increment, oldVec.y + sine*increment]; IF drawIt THEN context.MaskVector[oldVec, newVec] ELSE IF Almost[state.progress, stepSize] THEN { state.step _ (state.step + 1) MOD MaxStep; state.progress _ 0.0; }; oldVec _ newVec; ENDLOOP; }; }; -- DrawLineSeg Init: PROC [] = { FOR f: FontType IN FontType DO FOR o: OutputType IN OutputType DO extents: ImagerFont.Extents _ ImagerFont.FontBoundingBox[textFont[f][o]]; fontHeight[f][o] _ extents.descent+extents.ascent; ENDLOOP; ENDLOOP; }; -- Init Init[]; }. CHANGE LOG. Chen, August 2, 1985 7:08:09 pm PDT, spin off from PlotViewer. fPlotViewerPaint.mesa Last Edited by: Sweetsun Chen, August 2, 1985 9:28:51 pm PDT constants (constant) variables type press context has been rotated title / legend / footnote axes and curves external vars referenced: output. external vars referenced: output. Press doesn't support MaskStroke. context.SetColor[foreground]; -- already done. ... bad! ʘJšÏc™šœ™Jšœ,™,J™—šÏk ˜ Jšœ žœžœ˜JšœžœÓžœ˜äJšœžœ˜(Jšœ žœ@˜PJšžœžœ˜#Jšœžœ7˜AJšœžœ|˜‰Jšœžœ˜#Jšœžœ˜"Jšœžœžœ ˜*Jšœžœ˜Jšœžœ˜%J˜—šœžœž˜Jšžœ0žœ&˜_Jšžœ ˜—J˜Jšžœ˜J˜J™ Jšœžœ˜Jšœžœ˜Jšœžœ˜J˜J™Jšœ?žœ˜Mšœ*˜*Jšœ:˜:—Jšœ;˜;šœ(˜(Jšœ8˜8—Jš œ ž œ žœžœ žœ:˜pJš œ ž œ žœžœ žœžœ˜>J˜J™Jšœ žœ˜!J˜šÏnœž œ8žœžœ˜kJšœ™š Ÿ œžœžœžœžœ˜=Jšžœžœ˜4Jšœ˜—J˜šœžœžœ˜SJšžœ#˜'—JšœE˜Ešœ#žœžœ ž˜IJšžœžœžœ ˜JJšžœ ˜—J˜š žœžœžœžœ,žœ˜PJ˜šœ#žœžœ ž˜Hšžœž˜Jšœ>žœ ˜R——J˜Jšœžœ ˜Jšœžœ ˜J˜Jšœ žœ˜Jšœ žœ˜Jšœ žœ˜"Jšœ žœ˜#J˜Jšœžœ˜!Jšœžœ˜!Jšœžœ˜!Jšœžœ˜ Jšœ žœ˜&Jšœ žœ˜(J˜Jšœ žœ˜%Jšœžœ0˜DJšœžœ ˜2Jš œ žœžœžœžœ˜HJš œ žœžœžœžœ˜BJ˜Jšœ3˜3Jšœ7˜7Jšœ7˜7J˜šžœžœ˜-J˜J˜Jšœ˜—J˜Jšœ™J˜šžœžœžœ˜Jšœžœ"˜+Jš œžœžœ žœžœ&˜eJš œžœžœžœžœ˜gJšœ=˜=šœE˜EJšœ$˜$—J˜—J˜Jšœ™šžœ%žœ˜-Jšœ žœ˜Jšœc˜cšœ˜JšœŽ˜Ž—šžœ#žœžœž˜6Jšœžœ ˜Jšžœ˜—Jšœ'˜'J˜—J˜—š žœžœžœžœžœ˜7Jšœ žœ˜!šžœžœ žœ˜5J˜J˜Jš žœžœžœ žœžœžœ˜)J˜Jšžœžœžœžœ˜šžœžœ˜Jšœžœ˜Jšœ žœB˜OJšœžœ˜Jšœ žœB˜OJšœžœ˜"Jšœžœ˜"J˜šœ˜JšœŽ˜Ž—Jšœ5˜5Jšœ˜Jšœ%˜%šžœžœ˜Jšœ˜šžœžœžœž˜&Jšœžœ ˜šœ˜Jšœ?˜?Jšœžœ˜(—Jšžœ˜—J˜—šžœžœžœž˜+Jšœžœ ˜Jšœžœ ˜,šœ˜Jšœ@˜@—Jšžœ˜—J˜—J˜—J˜—Jšœ ˜ —J˜šŸ œžœ8žœ˜dJšœžœ*˜4Jšœžœ4˜>J˜Jšžœžœžœ˜šžœ#žœ˜*JšœK˜K—šžœžœ$ž˜/JšœM˜M—Jšœ ˜J˜—šŸœžœG˜UJšžœ'˜.J˜šŸœžœ˜Jšœ˜J˜Jšœ)˜)šžœžœ˜Jš$˜$JšœS˜SJšœS˜SJšœS˜SJšœS˜SJ˜—šžœ˜šœ˜J˜'J˜'J˜'J˜'J˜—Jšœ-žœ˜3J˜—Jšœ ˜ J˜—šŸœžœ+˜EJšœžœ˜ Jšœ!™!Jšœ žœ˜,Jšœ žœ!˜4Jšœ žœ˜+Jšœžœ˜ J˜šžœžœžœž˜$Jšœ˜šœ˜Jšžœ žœ˜&Jšœ˜Jšœ˜—šžœžœžœ˜!Jšœžœ˜Jšœžœ ˜Jšœžœ˜ JšœG˜GJšœžœ+žœ˜=Jšžœžœ1˜GJšžœ˜Jšœ.˜.Jšžœžœ1˜GJšžœ˜J˜—Jšžœ˜Jšœ˜J˜——šŸœžœ+˜CJšœžœ˜ Jšœ!™!Jšœ žœ˜,Jšœ žœ!˜4Jšœ žœ˜,Jšœžœ˜ J˜šžœžœžœž˜$Jšœ˜˜Jšžœ žœ˜&Jšœ˜Jšœ˜—šžœžœžœ˜!Jšœžœ˜Jšœžœ ˜Jšœžœ˜ JšœG˜GJšœžœ+žœ˜=Jšžœžœ1˜GJšžœ˜Jšœ.˜.Jšžœžœ1˜GJšžœ˜J˜——Jšžœ˜Jšœ˜—J˜Jšœžœ˜ Jšœžœ˜ Jšœžœ˜&Jšœžœ˜Jšœžœ˜JšœQžœ˜VJšœžœ˜J˜šœ*žœ žœ˜OJšœ˜—šœ*žœ žœ˜OJšœ˜—Jšœžœ˜,Jšœžœ˜/J˜šœ*žœ žœ˜OJšœ˜—šœ*žœ žœ˜OJšœ˜—Jšœžœ˜,Jšœžœ˜/J˜šœ ˜ Jšœ.˜.Jšœ2˜2Jšœ˜Jšœ˜—Jšžœžœ˜?Jšžœžœ˜?J˜Jšœ'˜'Jšœ'˜'J˜Jšœ˜J˜Jšœ žœžœ+˜@Jšœ žœžœ,˜AJ˜šœ>˜>Jšœ,˜,—šœ>˜>Jšœ,˜,—J˜Jšœ˜Jšœ%˜%JšœY˜YJšœW˜WJšœ ˜J˜—š Ÿ œžœ žœ<žœ žœ˜xJšœ2˜2JšœE˜EJšœ.˜.Jšœ&˜&Jšœ˜J˜—šŸ œžœ,žœ žœ˜SJšžœžœ˜)J˜:Jšœ#žœ˜*Jšœ#žœ˜)Jšžœžœ&˜>J˜!šœ ˜J˜——š Ÿ œžœ žœ žœžœžœ˜JJšœ žœ˜Jšœžœ˜Jšœžœ˜Jšœžœžœžœ#˜?J˜Jšžœžœ˜)J˜J˜$J˜%J˜%šžœžœ˜Jšœ$˜$J˜—J˜/šžœžœžœž˜Jšœ˜Jšžœžœžœž˜4Jšžœ˜—š žœžœžœžœž˜SJšžœžœžœžœ˜>—Jšœžœ ˜Jšœ˜J˜—š Ÿœžœ žœ žœžœžœ˜GJšœžœžœ˜Jšœžœ˜Jšœžœ˜ J˜Jšžœ žœžœ˜Jšžœ žœ˜#J˜'J˜Jšžœžœ ˜+šžœžœ˜Jšžœžœžœžœ˜:Jšžœžœžœžœ˜;—Jšžœ žœ ˜šœ ˜J˜——š Ÿœžœžœžœžœžœ˜6Jš œžœžœžœžœ˜ Jšžœ žœžœ˜1Jšœ ˜ J˜—šŸ œžœ˜*Jšœ žœ žœ˜J˜-J˜/Jšœ˜Jšœ˜Jšœžœ˜Jšžœ žœ˜J˜šœžœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ"˜"Jšœ˜Jšœ˜ J˜—Jšœ˜Jšœžœ˜ Jšœ3˜3J˜Jšžœžœžœ ˜(Jšœ=˜=šœA˜AJšœ/˜/—Jšœ1˜1Jšœžœ!˜*šœžœž˜ Jšœ&˜&Jšœ+˜+Jšžœ˜ —šœžœž˜ Jšœ"˜"Jšœ)˜)Jšžœ˜—Jšœ˜šžœžœžœ˜ ˜IJšœ# ˜.Jšœ/˜>Jšœ"˜1Jšœ*˜=Jšœ$˜4Jšœ4˜LJšœ2˜BJšœ+˜?J˜J˜—šŸ œžœ˜*Jšœžœžœ˜ Jšœ˜šœžœ˜J™—Jšžœžœžœžœ˜9Jšžœ žœžœ žœ˜3J™Jšžœ žœ˜,šžœD˜KJšœžœ˜Jšœžœ˜Jšœ žœ˜/Jšœžœ˜Jšœžœ˜Jšœžœ˜ Jšœ žœ˜%Jšœžœ˜ šžœž˜ Jšœ-˜-Jšœ/˜/šžœžœ˜$Jšœ žœ˜Jšœ žœ)˜8J˜—šžœ˜Jšœ žœ˜Jšœ žœ(˜7J˜—Jšœ$˜$Jšœ,˜,JšœB˜BJšžœžœ#˜1šžœžœ"žœ˜/Jšœžœ ˜*J˜J˜—Jšœ˜Jšžœ˜—Jšœ˜—Jšœ˜—J˜˜šžœ žœ ž˜šžœžœ ž˜"JšœI˜IJšœ2˜2Jšžœ˜—Jšžœ˜—Jšœ˜ —J˜J˜J˜J˜šžœžœ˜ J˜>——…—Qi