DIRECTORY Draw2d, Imager USING [Context, SetColor, ColorOperator, MaskRectangle, SetFont, SetStrokeEnd, SetStrokeJoint, SetStrokeWidth, MaskVector, Color, Font, SetXY, ShowChar, ShowRope, DoSaveAll, RotateT, Move, black, white], ImagerBackdoor USING [GetBounds, invert], ImagerColor USING [NewColorOperatorRGB, ColorFromRGB], ImagerDitherContext USING [MakeSpecialColor], ImagerFont USING [Extents, Scale, Find, RopeEscapement, RopeBoundingBox, FontBoundingBox], ImagerInterpress USING [Ref, Create, DeclareColorOperator, DeclareFont, DoPage, Close], Menus USING [Menu, CreateMenu, AppendMenuEntry, ReplaceMenuEntry, CreateEntry, MouseButton, MenuEntry], ViewerClasses USING [Viewer, ViewerRec, ViewerClass, ViewerClassRec, NotifyProc, PaintProc, ModifyProc, DestroyProc, ScrollProc, HScrollProc, AdjustProc, GetProc], ViewerOps USING [CreateViewer, PaintViewer, RegisterViewerClass, DestroyViewer], TIPUser USING [InstantiateNewTIPTable ,TIPScreenCoords], Icons USING [NewIconFromFile], InputFocus USING [GetInputFocus, SetInputFocus], IO USING [PutFR, real], Convert USING [RopeFromInt], Vector2 USING [VEC], Rope USING [ROPE, Cat, Length, InlineFetch], Real USING [InlineFixI, InlineFix, Floor], TerminalIO USING [PutRopes], PlotGraph; PlotGraphImpl: CEDAR MONITOR IMPORTS Convert, Draw2d, Icons, Imager, ImagerBackdoor, ImagerColor, ImagerDitherContext, ImagerFont, ImagerInterpress, InputFocus, IO, Menus, Real, Rope, TerminalIO, TIPUser, ViewerOps EXPORTS PlotGraph ~ BEGIN OPEN PlotGraph; VEC: TYPE ~ Vector2.VEC; PlotList: TYPE ~ LIST OF Plot; AxisList: TYPE ~ LIST OF Axis; GraphList: TYPE ~ LIST OF Graph; Viewer: TYPE ~ ViewerClasses.Viewer; Color: TYPE ~ Imager.Color; ViewerData: TYPE ~ REF ViewerDataRec; ViewerDataRec: TYPE ~ RECORD [ plot: Plot, -- circular references... liasonEntry: ARRAY BOOLEAN OF Menus.MenuEntry, magEntry: ARRAY BOOLEAN OF Menus.MenuEntry, xScale: REAL _ 0.125, --mag plot width / plot width xOr: REAL _ 0.0, --mag origin _ plot origin+ plot width*xOr frozen: BOOLEAN _ FALSE,--the display is no longer refreshed by outside requests grid: BOOLEAN _ FALSE, --an oscilloscope grid is painted on the viewer (if axis are coherent) magOn: BOOL _ FALSE, --state of the magnifier bw: BOOL _ FALSE, --black and white substitution of colors upToDate: BOOL _ FALSE --the data is valid for painting ]; PaintData: TYPE ~ REF PaintDataRec; PaintDataRec: TYPE ~ RECORD [ window: Rectangle, plotOr: VEC, -- physical coordinates or: VEC -- client coordinates ]; debugPlot: Plot _ NIL; debugAxis: Axis _ NIL; debugGraph: Graph _ NIL; CreatePlot: PUBLIC PROC [name: ROPE _ NIL] RETURNS [plot: Plot] ~ { plot _ NEW[PlotRec]; plot.name _ NEW[ROPE _ name]; plot.private _ NEW[PlotGraphPrivateRec]; CreatePlotViewer[plot]; }; CreatePlotViewer: PROC [plot: Plot] ~ { menu: Menus.Menu; resetEntry: Menus.MenuEntry _ Menus.CreateEntry[ name: "Reset", proc: SetDefaults, clientData: NIL, documentation: "Reset graphViewer" ]; magOffEntry: Menus.MenuEntry _ Menus.CreateEntry[ name: "MagOff", proc: MagOnOff, clientData: false, documentation: "Magnifier switch" ]; magOnEntry: Menus.MenuEntry _ Menus.CreateEntry[ name: "MagOn", proc: MagOnOff, clientData: true, documentation: "Magnifier switch" ]; gratEntry: Menus.MenuEntry _ Menus.CreateEntry[ -- which correspond to the "grid" things name: "Grat", proc: GridOnOff, clientData: NIL, documentation: "Draw time graticule" ]; shotEntry: Menus.MenuEntry _ Menus.CreateEntry[ name: "I.P.", proc: IPBut, clientData: NIL, documentation: "Creates Interpress master" ]; activeEntry: Menus.MenuEntry _ Menus.CreateEntry[ name: "Active", proc: Freeze, clientData: true, documentation: "Stops interactive refresh" ]; frozenEntry: Menus.MenuEntry _ Menus.CreateEntry[ name: "Frozen", proc: Freeze, clientData: false, documentation: "Stops interactive refresh" ]; sizeXEntry: Menus.MenuEntry _ Menus.CreateEntry[ name: "sizeX", proc: ScaleX, clientData: NIL, documentation: "Zoom X" ]; magSizeEntry: Menus.MenuEntry _ Menus.CreateEntry[ name: "MagSizeX", proc: MagScaleX, clientData: NIL, documentation: "Zoom X mag" ]; sizeYEntry: Menus.MenuEntry _ Menus.CreateEntry[ name: "sizeY", proc: ScaleY, clientData: NIL, documentation: "Zoom Y" ]; viewerData: ViewerData _ NEW[ViewerDataRec]; viewerData.magEntry _ [magOffEntry, magOnEntry]; viewerData.liasonEntry _ [frozenEntry, activeEntry]; plot.private _ NEW[PlotGraphPrivateRec]; viewerData.plot _ plot; menu _ Menus.CreateMenu[]; Menus.AppendMenuEntry[menu: menu, entry: resetEntry]; Menus.AppendMenuEntry[menu: menu, entry: magOffEntry]; Menus.AppendMenuEntry[menu: menu, entry: gratEntry]; Menus.AppendMenuEntry[menu: menu, entry: shotEntry]; Menus.AppendMenuEntry[menu: menu, entry: activeEntry]; Menus.AppendMenuEntry[menu: menu, entry: sizeXEntry]; Menus.AppendMenuEntry[menu: menu, entry: magSizeEntry]; Menus.AppendMenuEntry[menu: menu, entry: sizeYEntry]; plot.private.viewer _ ViewerOps.CreateViewer[ flavor: $PlotGViewer, info: [ name: plot.name^, menu: menu, iconic: TRUE, column: left, scrollable: TRUE, hscrollable: TRUE, border: TRUE, guardDestroy: TRUE, -- window menu guards destroy data: viewerData ] ]; }; CreateChild: PUBLIC PROC [mom: Plot] RETURNS [baby: Plot] ~ { tempAxis: AxisList; baby _ NEW[PlotRec _ mom^]; baby.name _ NEW[ROPE _ Rope.Cat[mom.name^, "+"]]; baby.axis _ LIST[NIL]; tempAxis _ baby.axis; FOR iAxisList: AxisList _ mom.axis, iAxisList.rest UNTIL iAxisList=NIL DO tempAxis.rest _ LIST[NEW[AxisRec _ iAxisList.first^]]; tempAxis _ tempAxis.rest; ENDLOOP; baby.axis _ baby.axis.rest; }; AwakeOthers: ENTRY PROC [plot: Plot] ~ { ENABLE UNWIND => NULL; BROADCAST plot.private.unlocked; }; LockPlot: PUBLIC ENTRY PROC [plot: Plot] ~{ ENABLE UNWIND => NULL; IF plot=NIL THEN RETURN; IF plot.private=NIL THEN RETURN; WHILE plot.private.locked DO WAIT plot.private.unlocked; ENDLOOP; IF plot.private=NIL THEN RETURN; -- If somebody destroyed the plot meanwhile plot.private.locked _ TRUE; }; UnlockPlot: PUBLIC ENTRY PROC [plot: Plot]~{ ENABLE UNWIND => NULL; IF plot=NIL THEN RETURN; IF plot.private=NIL THEN RETURN; plot.private.locked _ FALSE; BROADCAST plot.private.unlocked; }; RefreshPlot: PUBLIC PROC [plot: Plot, axis: AxisList _ NIL, graphs: GraphList _ NIL, within: Rectangle _ WorldRectangle, eraseFirst: BOOL _ FALSE] ~ { viewerData: ViewerData; IF plot.private=NIL THEN CreatePlotViewer[plot]; IF plot.private.viewer.iconic THEN RETURN; viewerData _ NARROW[plot.private.viewer.data]; IF viewerData.frozen THEN RETURN; viewerData.upToDate _ FALSE; RefreshScreen[plot, axis, graphs, within, eraseFirst]; }; PaintGrid: PROC [context: Imager.Context, plot: Plot, page: Rectangle, d: REAL, green, blue: Imager.Color] ~ { x, y: REAL; unit: REAL _ NormalizeScaleAndOrigin[plot]; Imager.SetColor[context, green]; x _ page.x; y _ page.h+gridFactor*page.y; Imager.MaskVector[context, [x, y], [x+page.w, y]]; y _ y-3.0; FOR i: INT IN [1..19] DO x _ page.x+i*page.w/20.0; Imager.MaskVector[context, [x, y], [x, y+6.0]]; ENDLOOP; ShowRope[page.x+page.w/20.0, y+8.0, IO.PutFR["u:%g", IO.real[unit]], context, blue]; ShowRope[page.x+4.0, y-d, IO.PutFR["%g", IO.real[plot.axis.first.bounds.x]], context, blue] }; PaintTextInContext: PROC [context: Imager.Context, plot: Plot, page: Rectangle, font: Imager.Font] ~ { FOR iTextList: LIST OF PlotText _ plot.texts, iTextList.rest UNTIL iTextList=NIL DO text: PlotText ~ iTextList.first; IF text.wrt=NIL THEN { RotateAndShow: PROC ~ { Imager.SetXY[context, [page.x+xScaled, page.y+yScaled]]; Imager.Move[context]; Imager.RotateT[context, text.rotation]; Imager.SetXY[context, [x, y]]; Imager.ShowRope[context, text.contents]; }; x, y: REAL; xScaled: REAL _ text.bounds.x*page.w; yScaled: REAL _ text.bounds.y*page.h; wScaled: REAL _ text.bounds.w*page.w; hScaled: REAL _ text.bounds.h*page.h; ropeSize: ImagerFont.Extents _ ImagerFont.RopeBoundingBox[font, text.contents]; x _ SELECT iTextList.first.justifyX FROM left => 0.0, center => MAX[0.0, (wScaled-ropeSize.leftExtent)*0.5], right => MAX[0.0, wScaled-ropeSize.leftExtent], ENDCASE => 0.0; y _ SELECT iTextList.first.justifyY FROM bottom => 0.0, center => MAX[0.0, (hScaled-ropeSize.descent)*0.5], top => MAX[0.0, hScaled-ropeSize.descent], ENDCASE => 0.0; Imager.DoSaveAll[context, RotateAndShow]; }; ENDLOOP; }; PaintContext: PROC [context: Imager.Context, plot: Plot, axis: AxisList, graphs: GraphList, within, page: Rectangle, font: Imager.Font, eraseFirst, color, grid: BOOL _ FALSE] ~ { WritePtV: PROC [x, y: REAL, rope: ROPE _ NIL] RETURNS [quit: BOOL _ FALSE] ~ { x0, y0: REAL; n: INT; r: ROPE; n _ Real.InlineFix[y]; IF (Real.InlineFix[prevPt.y] = n) AND rope=NIL THEN RETURN; x0 _ (x+or.x)*mult.x+window.x; IF x0window.x+window.w THEN RETURN; y0 _ window.y; Draw2d.Line[context, [x0, y0], [x0, y0+tinyTick], solid, zip]; r _ IF rope~=NIL THEN rope ELSE Convert.RopeFromInt[n, 16, FALSE]; ShowRopeV[context, r, [x0 - 2.0, y0+spaceOverTick], font]; prevPt _ [x0, y]; }; WritePtH: PROC [x, y: REAL, rope: ROPE _ NIL] RETURNS [quit: BOOL _ FALSE] ~ { x0, y0: REAL; n: INT; r: ROPE; n _ Real.InlineFix[y]; IF (Real.InlineFix[prevPt.y] = n) AND rope=NIL THEN RETURN; x0 _ (x+or.x)*mult.x+window.x; IF x0window.x+window.w THEN RETURN; y0 _ window.y; Draw2d.Line[context, [x0, y0], [x0, y0+tinyTick], solid, zip]; Imager.SetXY[context, [x0 - 2.0, y0+spaceOverTick]]; r _ IF rope~=NIL THEN rope ELSE Convert.RopeFromInt[n, 16, FALSE]; Imager.ShowRope[context, r]; prevPt _ [x0, y]; }; DrawAndMarkPt: PROC [x, y: REAL, rope: ROPE _ NIL] RETURNS [quit: BOOL _ FALSE] ~ { IF DrawPt[x, y, rope] THEN RETURN[TRUE]; IF previousVisible THEN Draw2d.Mark[context, prevPt, dot]; }; DrawPt: PROC [x, y: REAL, rope: ROPE _ NIL] RETURNS [quit: BOOL _ FALSE] ~ { pt: VEC; visible: BOOLEAN; IF firstPoint THEN { prevPt _ [(x+or.x)*mult.x+window.x, (y+or.y)*mult.y+window.y]; firstPoint _ FALSE; previousVisible _ IsPtInArea[prevPt, window]; } ELSE { pt _ [(x+or.x)*mult.x+window.x, (y+or.y)*mult.y+window.y]; visible _ IsPtInArea[pt, window]; SELECT TRUE FROM visible AND previousVisible => Draw2d.Line[context, prevPt, pt, solid, zip]; visible AND ~previousVisible => Draw2d.Line[context, ClipVect[pt, prevPt, window], pt, solid, zip]; ~visible AND previousVisible => Draw2d.Line[context, prevPt, ClipVect[prevPt, pt, window], solid, zip]; ~visible AND ~previousVisible => { pt1, pt2: VEC; IF pt.y~=prevPt.y THEN { pt1 _ ClipVect[prevPt, pt, window]; pt2 _ ClipVect[pt, prevPt, window]; IF pt1.x~=pt2.x OR pt1.y~=pt2.y THEN Draw2d.Line[context, pt1, pt2, solid, zip]; } }; ENDCASE; prevPt _ pt; previousVisible _ visible; } }; axisFound, graphFound: BOOL; quit: BOOL _ FALSE; wwhite, rred, ggreen, bblue, ppuce: Imager.Color; charHeight: REAL _ ImagerFont.FontBoundingBox[font].ascent+1.0; axisChoice: BOOLEAN = ~(axis=NIL); --test only once the presence of an axis specification graphsChoice: BOOLEAN = ~(graphs=NIL); -- the same for graphs hAxis: ARRAY DrawingStyle OF REAL _ ALL[0.0]; selectedAxis: Axis _ GetSelectedAxis[plot]; window: Rectangle _ [page.x, page.y, page.w, 1.0]; or: VEC _ [0.0, 0.0]; prevPt: VEC _ [0.0, 0.0]; mult: VEC _ [1.0, 1.0]; firstPoint: BOOLEAN _ TRUE; previousVisible: BOOLEAN _ TRUE; zip: Draw2d.Zip; IF color THEN { wwhite _ grey; rred _ red; ggreen _ green; bblue _ blue; ppuce _ puce; } ELSE { wwhite _ white; rred _ black; ggreen _ black; bblue _ black; ppuce _ black; }; IF eraseFirst THEN { Imager.SetColor[context, bblue]; PaintTextInContext[context, plot, page, font]; }; IF plot.axis=NIL THEN RETURN; hAxis _ SetHeights[plot, charHeight, IF grid THEN page.h-page.y ELSE page.h]; IF grid AND eraseFirst THEN PaintGrid[context, plot, page, charHeight, ggreen, bblue]; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO thisAxis: Axis ~ iAxisList.first; xoffset, inter: REAL _ 0.0; colorList: LIST OF Color; IF axisChoice THEN { axisFound _ FALSE; FOR jAxisList: AxisList _ axis, jAxisList.rest UNTIL jAxisList=NIL DO IF jAxisList.first=thisAxis THEN { axisFound _ TRUE; EXIT; }; ENDLOOP; } ELSE axisFound _ TRUE; IF axisFound THEN { xoffset _ 2.0; colorList _ graphColorList; inter _ AxisHeight[thisAxis, hAxis]; or.x _ -thisAxis.bounds.x; or.y _ -thisAxis.bounds.y; mult.x _ page.w/thisAxis.bounds.w; mult.y _ (inter-charHeight-3.0)/thisAxis.bounds.h; window.h _ inter; IF eraseFirst THEN { IF thisAxis.axisData[X].visible THEN { Imager.SetColor[context, ggreen]; Imager.MaskVector[context, [window.x, window.y], [window.x+page.w, window.y]]; }; IF thisAxis.axisData[Y].visible THEN { Imager.SetColor[context, ggreen]; Imager.MaskVector[context, [window.x, window.y], [window.x, window.y+inter]]; }; IF thisAxis=selectedAxis THEN ShowRopeInv[window.x+xAxisName, window.y+yAxisName-charHeight, thisAxis.name, context, wwhite, bblue, font] ELSE ShowRopeInv[window.x+xAxisName, window.y+yAxisName-charHeight, thisAxis.name, context, bblue, wwhite, font]; }; FOR iGraphList: GraphList _ thisAxis.graphs, iGraphList.rest UNTIL iGraphList=NIL DO thisGraph: Graph ~ iGraphList.first; IF graphsChoice THEN { graphFound _ FALSE; FOR jGraphList: GraphList _ graphs, jGraphList.rest UNTIL jGraphList=NIL DO IF jGraphList.first=thisGraph THEN { graphFound _ TRUE; EXIT; }; ENDLOOP; IF ~graphFound THEN LOOP; }; firstPoint _ TRUE; Imager.SetColor[context, colorList.first]; zip _ Draw2d.GetZip[context]; quit _ thisGraph.class.enumerate[plot, thisGraph, within, SELECT thisAxis.style FROM hexaH => WritePtH, hexaV => WritePtV, mark => DrawAndMarkPt, ENDCASE => DrawPt]; Draw2d.ReleaseZip[zip]; IF quit THEN RETURN; IF eraseFirst THEN ShowRope[window.x+xoffset, window.y+inter*0.5, thisGraph.name, context, colorList.first]; xoffset _ xoffset+ImagerFont.RopeEscapement[font, thisGraph.name].x+5.0; IF colorList.rest#NIL THEN colorList _ colorList.rest ELSE colorList _ graphColorList; ENDLOOP; }; window.y _ window.y+inter; ENDLOOP; }; RefreshScreen: PROC [plot: Plot, axis: AxisList _ NIL, graphs: GraphList _ NIL, within: Rectangle _ WorldRectangle, eraseFirst: BOOL _ FALSE] ~ { DoRefreshScreen: PROC [viewer: Viewer, context: Imager.Context] ~ { grid: BOOL _ viewerData.grid; color: BOOL _ viewer.column=color; page: Rectangle _ [xSpace, ySpace, viewer.cw-2*xSpace, viewer.ch-2*ySpace]; Imager.SetColor[context, IF color THEN grey ELSE white]; Imager.MaskRectangle[context, ImagerBackdoor.GetBounds[context]]; -- fill screen Imager.SetFont[context, currentFont]; PaintContext[context, plot, axis, graphs, within, page, currentFont, eraseFirst, color, grid]; IF ~viewerData.magOn AND eraseFirst THEN DrawMag[viewer, context]; }; viewerData: ViewerData _ NARROW[plot.private.viewer.data]; IF plot#viewerData.plot THEN ERROR; DrawInViewer[plot.private.viewer, DoRefreshScreen, FALSE]; }; ProduceIPMaster: PUBLIC PROC [plot: Plot] ~ { DoPrint: PROC [context: Imager.Context] ~ { grid: BOOL _ viewerData.grid; color: BOOL _ ipInColor; Imager.SetStrokeWidth[context, fineStroke]; Imager.SetStrokeEnd[context, round]; Imager.SetStrokeJoint[context, round]; Imager.SetFont[context, pressFont]; PaintContext[context, plot, NIL, NIL, within, pageSize, pressFont, TRUE, color, grid]; }; viewerData: ViewerData _ NARROW[plot.private.viewer.data]; fileName: Rope.ROPE _ Rope.Cat["///Temp/PlotGraph/", plot.name^, ".interpress"]; within: Rectangle _ [plot.lowerBounds.x, plot.lowerBounds.y, plot.upperBounds.x-plot.lowerBounds.x, plot.upperBounds.y - plot.lowerBounds.y]; ip: ImagerInterpress.Ref _ ImagerInterpress.Create[fileName]; rgbLinear: Imager.ColorOperator ~ ImagerColor.NewColorOperatorRGB[255]; ImagerInterpress.DeclareColorOperator[ip, rgbLinear]; ImagerInterpress.DeclareFont[ip, pressFont]; ImagerInterpress.DoPage[ip, DoPrint, pressScale]; ImagerInterpress.Close[ip]; TerminalIO.PutRopes[fileName, " created\n"]; }; DeletePlot: PUBLIC PROC [plot: Plot] ~ { viewer: Viewer _ plot.private.viewer; ViewerOps.DestroyViewer[viewer]; }; SetDefaults: PROC [parent: REF ANY, clientData: REF ANY, mouseButton: Menus.MouseButton, shift, control: BOOL] ~ { viewer: Viewer ~ NARROW[parent]; viewerData: ViewerData ~ NARROW[viewer.data]; plot: Plot _ viewerData.plot; IF viewerData.magOn THEN MagOnOff[viewer, true, yellow, FALSE, FALSE]; LockPlot[plot]; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO iAxisList.first.bounds.x _ plot.lowerBounds.x; iAxisList.first.bounds.y _ plot.lowerBounds.y; iAxisList.first.bounds.w _ plot.upperBounds.x - plot.lowerBounds.x; iAxisList.first.bounds.h _ plot.upperBounds.y - plot.lowerBounds.y; ENDLOOP; UnlockPlot[plot]; RefreshScreen[plot: plot, within: WorldRectangle, eraseFirst: TRUE]; }; GridOnOff: PROC [parent: REF ANY, clientData: REF ANY, mouseButton: Menus.MouseButton, shift, control: BOOL] ~ { viewer: Viewer ~ NARROW[parent]; viewerData: ViewerData ~ NARROW[viewer.data]; viewerData.grid _ ~viewerData.grid; RefreshScreen[plot: viewerData.plot, within: WorldRectangle, eraseFirst: TRUE] }; IPBut: PROC [parent: REF ANY, clientData: REF ANY, mouseButton: Menus.MouseButton, shift, control: BOOL] ~ { viewer: Viewer ~ NARROW[parent]; viewerData: ViewerData ~ NARROW[viewer.data]; ProduceIPMaster[viewerData.plot]; }; Freeze: PROC [parent: REF ANY, clientData: REF ANY, mouseButton: Menus.MouseButton, shift, control: BOOL] ~ { viewer: Viewer ~ NARROW[parent]; viewerData: ViewerData ~ NARROW[viewer.data]; plot: Plot ~ viewerData.plot; liasonState: BOOLEAN _ NARROW[clientData, REF BOOLEAN]^; Menus.ReplaceMenuEntry[viewer.menu, viewerData.liasonEntry[liasonState], viewerData.liasonEntry[~liasonState]]; viewerData.frozen _ ~viewerData.frozen; ViewerOps.PaintViewer[viewer, menu]; }; MagScaleX: PROC [parent: REF ANY, clientData: REF ANY, mouseButton: Menus.MouseButton, shift, control: BOOL] ~ { viewer: Viewer ~ NARROW[parent]; viewerData: ViewerData ~ NARROW[viewer.data]; plot: Plot ~ viewerData.plot; alpha: REAL; IF mouseButton=yellow THEN RETURN; alpha _ IF mouseButton=red THEN 0.5 ELSE 2.0; IF shift THEN alpha _ alpha*alpha; IF viewerData.xScale*alpha>1.0 THEN RETURN; IF viewerData.magOn THEN { viewerData.xScale _ viewerData.xScale*alpha; PlotScaleX[plot, alpha]; RefreshScreen[plot: plot, within: WorldRectangle, eraseFirst: TRUE] } ELSE { MagExpand: PROC [viewer: Viewer, context: Imager.Context] ~ { zip: Draw2d.Zip; viewerData: ViewerData ~ NARROW[viewer.data]; x: REAL _ viewer.cw*(viewerData.xOr+viewerData.xScale); Imager.SetColor[context, tempColor]; zip _ Draw2d.GetZip[context]; Draw2d.Line[context, [x, 0.0], [x, viewer.ch], solid, zip]; viewerData.xScale _ viewerData.xScale*alpha; x _ viewer.cw*(viewerData.xOr+viewerData.xScale); Draw2d.Line[context, [x, 0.0], [x, viewer.ch], solid, zip]; Draw2d.ReleaseZip[zip]; }; DrawInViewer[viewer, MagExpand, FALSE]; }; }; ScaleX: PROCEDURE [parent: REF ANY, clientData: REF ANY, mouseButton: Menus.MouseButton, shift, control: BOOL] ~ { viewer: Viewer ~ NARROW[parent]; viewerData: ViewerData ~ NARROW[viewer.data]; plot: Plot ~ viewerData.plot; alpha: REAL _ 2.0; IF mouseButton=yellow THEN RETURN; IF mouseButton=red THEN alpha _ 0.5; IF shift THEN alpha _ alpha*alpha; viewerData.xScale _ MIN[1.0, viewerData.xScale/alpha]; IF ~viewerData.magOn THEN { PlotScaleX[plot, alpha]; RefreshScreen[plot: plot, within: WorldRectangle, eraseFirst: TRUE] }; }; ScaleY: PROCEDURE [parent: REF ANY, clientData: REF ANY, mouseButton: Menus.MouseButton, shift, control: BOOL] ~ { viewer: Viewer ~ NARROW[parent]; viewerData: ViewerData ~ NARROW[viewer.data]; plot: Plot ~ viewerData.plot; alpha: REAL _ 2.0; IF mouseButton=yellow THEN RETURN; IF mouseButton=red THEN alpha _ 0.5; IF shift THEN alpha _ alpha*alpha; LockPlot[plot]; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO iAxisList.first.bounds.h _ alpha*iAxisList.first.bounds.h; ENDLOOP; UnlockPlot[plot]; RefreshScreen[plot: plot, within: WorldRectangle, eraseFirst: TRUE] }; MagOnOff: PROCEDURE [parent: REF ANY, clientData: REF ANY, mouseButton: Menus.MouseButton, shift, control: BOOL] ~ { viewer: Viewer ~ NARROW[parent]; viewerData: ViewerData ~ NARROW[viewer.data]; plot: Plot ~ viewerData.plot; magState: BOOLEAN _ NARROW[clientData, REF BOOLEAN]^; IF magState#viewerData.magOn THEN ERROR; -- ????? Menus.ReplaceMenuEntry[viewer.menu, viewerData.magEntry[magState], viewerData.magEntry[~magState]]; ViewerOps.PaintViewer[viewer, menu]; IF viewerData.magOn THEN { LockPlot[plot]; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO iAxisList.first.bounds.w _ iAxisList.first.bounds.w/viewerData.xScale; iAxisList.first.bounds.x _ iAxisList.first.bounds.x-iAxisList.first.bounds.w*viewerData.xOr; ENDLOOP; UnlockPlot[plot]; } ELSE { LockPlot[plot]; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO iAxisList.first.bounds.x _ iAxisList.first.bounds.x+iAxisList.first.bounds.w*viewerData.xOr; iAxisList.first.bounds.w _ viewerData.xScale*iAxisList.first.bounds.w; ENDLOOP; UnlockPlot[plot]; }; viewerData.magOn _ ~viewerData.magOn; RefreshScreen[plot: plot, within: WorldRectangle, eraseFirst: TRUE]; }; PaintProc: ViewerClasses.PaintProc ~ { -- repaint screen for updates SELECT whatChanged FROM NIL => { -- window resized, redraw viewerData: ViewerData ~ NARROW[self.data]; RefreshScreen[plot: viewerData.plot, within: WorldRectangle, eraseFirst: TRUE] }; ENDCASE => { -- call path for DrawInViewer NARROW[whatChanged, REF PROC[Viewer, Imager.Context]]^[self, context]; }; }; NotifyProc: ViewerClasses.NotifyProc ~ { x, y, t, v: REAL; viewerData: ViewerData ~ NARROW[self.data]; plot: Plot ~ viewerData.plot; IF InputFocus.GetInputFocus[].owner#self THEN InputFocus.SetInputFocus[self, plot]; IF ISTYPE[input.first, TIPUser.TIPScreenCoords] -- If input is coords from mouse THEN { mousePos: TIPUser.TIPScreenCoords _ NARROW[input.first]; x _ mousePos.mouseX; y _ mousePos.mouseY; IF ISTYPE[input.rest.first, ATOM] THEN { actionName: ATOM _ NARROW[input.rest.first]; thisAxis: Axis; [thisAxis, t, v] _ GetAxis[self, x, y]; SELECT actionName FROM $Refresh => { RefreshScreen[plot: plot, within: WorldRectangle, eraseFirst: TRUE] }; $WriteCoord => IF thisAxis#NIL THEN{ IF thisAxis.style=analog THEN DrawRope[[x, y], IO.PutFR["t:%g,v:%g", IO.real[t], IO.real[v]], self, IF self.column= color THEN red ELSE black, currentFont] ELSE DrawRope[[x, y], IO.PutFR["t:%d", IO.real[t]], self, IF self.column= color THEN red ELSE black, currentFont]; RETURN; }; $MoveMag => { MoveMag: PROC [viewer: Viewer, context: Imager.Context] ~ { zip: Draw2d.Zip; viewerData: ViewerData ~ NARROW[viewer.data]; xx: REAL _ viewer.cw*viewerData.xOr; Imager.SetColor[context, tempColor]; zip _ Draw2d.GetZip[context]; Draw2d.Line[context, [xx, 0.0], [xx, viewer.ch], solid, zip]; xx _ xx+viewer.cw*viewerData.xScale; Draw2d.Line[context, [xx, 0.0], [xx, viewer.ch], solid, zip]; viewerData.xOr _ x/viewer.cw; Draw2d.Line[context, [x, 0.0], [x, viewer.ch], solid, zip]; xx _ x+viewer.cw*viewerData.xScale; Draw2d.Line[context, [xx, 0.0], [xx, viewer.ch], solid, zip]; Draw2d.ReleaseZip[zip]; }; IF ~viewerData.magOn THEN DrawInViewer[self, MoveMag, FALSE]; }; $SelectAxis => { SelectAxis[plot, thisAxis]; }; $MoveAxis => { selectedAxis: Axis _ GetSelectedAxis[plot]; [] _ MoveAxis[plot, selectedAxis, thisAxis]; RefreshScreen[plot: plot, within: WorldRectangle, eraseFirst: TRUE] }; $CopyAxis => { selectedAxis: Axis _ GetSelectedAxis[plot]; [] _ InsertAxis[plot, CopyAxis[thisAxis], selectedAxis]; RefreshScreen[plot: plot, within: WorldRectangle, eraseFirst: TRUE] }; $RevMoveAxis => { selectedAxis: Axis _ GetSelectedAxis[plot]; [] _ MoveAxis[plot, thisAxis, selectedAxis]; RefreshScreen[plot: plot, within: WorldRectangle, eraseFirst: TRUE] }; $DelClickedAxis => { [] _ DeleteAxis[plot, thisAxis]; RefreshScreen[plot: plot, within: WorldRectangle, eraseFirst: TRUE] }; $XChange => { selectedAxis: Axis _ GetSelectedAxis[plot]; [] _ XChangeAxis[plot, thisAxis, selectedAxis]; RefreshScreen[plot: plot, within: WorldRectangle, eraseFirst: TRUE] }; $DelSelection => { DeleteSelection[plot]; RefreshScreen[plot: plot, within: WorldRectangle, eraseFirst: TRUE] }; $ChgStyle => { LockPlot[plot]; SELECT thisAxis.style FROM analog => thisAxis.style _ mark; mark => thisAxis.style _ analog; hexaH => thisAxis.style _ hexaV; hexaV => thisAxis.style _ hexaH; ENDCASE => ERROR; UnlockPlot[plot]; RefreshScreen[plot: plot, within: WorldRectangle, eraseFirst: TRUE] }; $Fork => { ForkAxis[plot, thisAxis]; RefreshScreen[plot: plot, within: WorldRectangle, eraseFirst: TRUE] }; $Join => { selectedAxis: Axis _ GetSelectedAxis[plot]; thisGraph: Graph _ thisAxis.graphs.first; IF thisGraph.name=NIL THEN thisGraph.name _ thisAxis.name; [] _ MoveGraphInAxis[plot, selectedAxis, thisGraph]; RefreshScreen[plot: plot, within: WorldRectangle, eraseFirst: TRUE] }; ENDCASE => { IF plot.eventProc#NIL THEN plot.eventProc[plot, thisAxis, [t, v], actionName]; }; }; }; }; ModifyProc: ViewerClasses.ModifyProc ~ { viewerData: ViewerData ~ NARROW[self.data]; plot: Plot ~ viewerData.plot; SELECT change FROM set => NULL; push => NULL; pop => NULL; kill => SelectAxis[plot, NIL]; ENDCASE; }; DestroyProc: ViewerClasses.DestroyProc ~ { viewerData: ViewerData ~ NARROW[self.data]; plot: Plot _ viewerData.plot; LockPlot[plot]; plot.axis _ NIL; AwakeOthers[plot]; plot.private _ NIL; }; GetProc: ViewerClasses.GetProc ~ { viewerData: ViewerData ~ NARROW[self.data]; plot: Plot _ viewerData.plot; WITH plot.selection.first SELECT FROM axis: Axis => RETURN[axis.name]; graph: Graph => RETURN[graph.name]; text: PlotText => RETURN[text.contents]; ENDCASE => NULL; }; VScrollProc: ViewerClasses.ScrollProc ~ { viewerData: ViewerData ~ NARROW[self.data]; plot: Plot _ viewerData.plot; IF plot.axis=NIL THEN RETURN; SELECT op FROM up => { LockPlot[plot]; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO iAxisList.first.bounds.y _ iAxisList.first.bounds.y - amount*iAxisList.first.bounds.h/self.ch; ENDLOOP; UnlockPlot[plot]; }; down => { LockPlot[plot]; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO iAxisList.first.bounds.y _ iAxisList.first.bounds.y + amount*iAxisList.first.bounds.h/self.ch; ENDLOOP; UnlockPlot[plot]; }; thumb => { newY: REAL _ amount*0.01*(plot.upperBounds.y-plot.lowerBounds.y)+plot.lowerBounds.y; LockPlot[plot]; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO iAxisList.first.bounds.y _ newY; ENDLOOP; UnlockPlot[plot]; }; query => { top _ 0; bottom _ 100; IF plot.upperBounds.y=plot.lowerBounds.y THEN RETURN; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO top _ MAX[top, Real.InlineFixI[100.0*(plot.upperBounds.y-iAxisList.first.bounds.y-iAxisList.first.bounds.h) /(plot.upperBounds.y-plot.lowerBounds.y)]]; bottom _ MIN[bottom, Real.InlineFixI[100.0*(plot.upperBounds.y-iAxisList.first.bounds.y) /(plot.upperBounds.y-plot.lowerBounds.y)]]; ENDLOOP; RETURN; }; ENDCASE; RefreshScreen[plot: plot, within: WorldRectangle, eraseFirst: TRUE] }; HScrollProc: ViewerClasses.HScrollProc ~ { viewerData: ViewerData ~ NARROW[self.data]; plot: Plot _ viewerData.plot; IF plot.axis=NIL THEN RETURN; SELECT op FROM left => { LockPlot[plot]; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO iAxisList.first.bounds.x _ iAxisList.first.bounds.x + amount*iAxisList.first.bounds.w/self.cw; ENDLOOP; UnlockPlot[plot]; }; right => { LockPlot[plot]; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO iAxisList.first.bounds.x _ iAxisList.first.bounds.x - amount*iAxisList.first.bounds.w/self.cw; ENDLOOP; UnlockPlot[plot]; }; thumb => { newX: REAL _ amount*0.01*(plot.upperBounds.x-plot.lowerBounds.x)+plot.lowerBounds.x; LockPlot[plot]; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO iAxisList.first.bounds.x _ newX; ENDLOOP; UnlockPlot[plot]; }; query => { left _ 0; right _ 100; IF plot.upperBounds.x=plot.lowerBounds.x THEN RETURN; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO left _ MAX[left, Real.InlineFixI[100.0*(iAxisList.first.bounds.x-plot.lowerBounds.x) /(plot.upperBounds.x-plot.lowerBounds.x)]]; right _ MIN[right, Real.InlineFixI[100.0*(iAxisList.first.bounds.x+iAxisList.first.bounds.w-plot.lowerBounds.x) /(plot.upperBounds.x-plot.lowerBounds.x)]]; ENDLOOP; RETURN; }; ENDCASE; RefreshScreen[plot: plot, within: WorldRectangle, eraseFirst: TRUE] }; AdjustProc: ViewerClasses.AdjustProc ~ { adjusted _ TRUE; }; DeleteSelection : PUBLIC PROC [plot: Plot] ~ { axisList: AxisList _ plot.axis; IF plot.selection=NIL THEN RETURN; UNTIL plot.selection=NIL DO WITH plot.selection.first SELECT FROM axis: Axis => DeleteAxis[plot, axis]; graph: Graph => NULL; point: GraphPoint => NULL; text: PlotText => DeleteText[plot, text]; ENDCASE => NULL; plot.selection _ plot.selection.rest; ENDLOOP; }; InsertAxis: PUBLIC PROC [plot: Plot, axis, after: Axis] ~ { axisList: AxisList _ plot.axis; IF axis=NIL THEN RETURN; LockPlot[plot]; IF after=NIL THEN plot.axis _ CONS[axis, plot.axis] ELSE { UNTIL axisList.first=after OR axisList=NIL DO axisList _ axisList.rest ENDLOOP; IF axisList=NIL THEN {UnlockPlot[plot]; RETURN}; axisList.rest _ CONS[axis, axisList.rest]; }; UnlockPlot[plot]; }; DeleteAxis: PUBLIC PROC [plot: Plot, axis: Axis] ~ { axisList: AxisList _ plot.axis; IF axisList=NIL THEN RETURN; LockPlot[plot]; IF axisList.first=axis THEN {plot.axis _ plot.axis.rest; UnlockPlot[plot]; RETURN}; UNTIL axisList.rest.first=axis DO axisList _ axisList.rest; IF axisList.rest=NIL THEN {UnlockPlot[plot]; RETURN}; ENDLOOP; axisList.rest _ axisList.rest.rest; UnlockPlot[plot]; }; MoveAxis: PUBLIC PROC [plot: Plot, axis, after: Axis] ~ { DeleteAxis[plot, axis]; InsertAxis[plot, axis, after]; }; XChangeAxis: PUBLIC PROC [plot: Plot, axis1, axis2: Axis] ~ { axisList: AxisList _ plot.axis; IF axisList=NIL THEN RETURN; LockPlot[plot]; UNTIL axisList=NIL DO SELECT axisList.first FROM axis1 => axisList.first _ axis2; axis2 => axisList.first _ axis1; ENDCASE; axisList _ axisList.rest; ENDLOOP; UnlockPlot[plot]; }; CopyAxis: PUBLIC PROC [axis: Axis] RETURNS [duplicate: Axis] ~ { duplicate _ NEW[AxisRec _ [ graphs: axis.graphs, bounds: axis.bounds, name: axis.name, style: axis.style, maxChars: axis.maxChars, axisData: axis.axisData ]]; }; ForkAxis: PUBLIC PROC [plot: Plot, axis: Axis] ~ { axisList: AxisList _ plot.axis; newAxisList, firstItem: AxisList; IF axisList=NIL THEN RETURN; IF axis.graphs.rest=NIL THEN RETURN; FOR iGraphList: GraphList _ axis.graphs, iGraphList.rest UNTIL iGraphList=NIL DO thisGraph: Graph _ iGraphList.first; newAxisList _ CONS[NEW[PlotGraph.AxisRec _ [ graphs: LIST[thisGraph], bounds: axis.bounds, name: thisGraph.name, style: axis.style, axisData: axis.axisData ]], newAxisList]; thisGraph.name _ NIL; IF firstItem=NIL THEN firstItem _ newAxisList; ENDLOOP; LockPlot[plot]; IF axisList.first=axis THEN { firstItem.rest _ plot.axis.rest; plot.axis _ newAxisList; } ELSE { UNTIL axisList.rest.first=axis DO axisList _ axisList.rest; IF axisList=NIL THEN {UnlockPlot[plot]; RETURN}; ENDLOOP; IF axisList.rest#NIL THEN firstItem.rest _ axisList.rest.rest; axisList.rest _ newAxisList; }; UnlockPlot[plot]; }; MoveGraphInAxis: PUBLIC PROC [plot: Plot, axis: Axis, graph: Graph] ~ { axisList: AxisList _ plot.axis; IF axis=NIL THEN RETURN; IF graph=NIL THEN RETURN; LockPlot[plot]; IF axis.graphs.first.name=NIL THEN { axis.graphs.first.name _ axis.name; axis.name _ "#"; }; axis.graphs _ CONS[graph, axis.graphs]; UnlockPlot[plot]; }; NormalizeScaleAndOrigin: PUBLIC PROC [plot: Plot] RETURNS[unit: REAL]~ { test, excess: REAL; unit _ Normalize[plot.axis.first.bounds.w/20.0]; excess _ plot.axis.first.bounds.x-Real.Floor[plot.axis.first.bounds.x/unit]*20.0; test _ plot.axis.first.bounds.w-Real.Floor[plot.axis.first.bounds.w/unit]*20.0; IF test=0.0 AND excess=0.0 THEN RETURN; LockPlot[plot]; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO iAxisList.first.bounds.x _ iAxisList.first.bounds.x - excess; iAxisList.first.bounds.w _ 20.0*unit; ENDLOOP; UnlockPlot[plot]; }; DrawInViewer: PROC [viewer: Viewer, proc: PROC [Viewer, Imager.Context], clear: BOOL _ FALSE] ~ { drawProc: REF PROC[Viewer, Imager.Context] _ NIL; TRUSTED { drawProc _ NEW[PROC[Viewer, Imager.Context] _ proc]; }; ViewerOps.PaintViewer[ viewer: viewer,-- pass record to viewer painter hint: client, whatChanged: drawProc, clearClient: clear ]; }; SetHeights: PROC [plot: Plot, charHeight, yActualSize: REAL] RETURNS [hAxis: ARRAY DrawingStyle OF REAL _ ALL[0.0]] ~ { nAxis, cAxis: ARRAY DrawingStyle OF REAL _ ALL[0.0]; inter: REAL; IF plot.axis=NIL THEN RETURN; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO nAxis[iAxisList.first.style] _ nAxis[iAxisList.first.style] + 1.0; cAxis[iAxisList.first.style] _ cAxis[iAxisList.first.style] + iAxisList.first.maxChars+1.0; ENDLOOP; inter _ yActualSize /(nAxis[analog]+nAxis[mark]+nAxis[hexaH]+cAxis[hexaV]); hAxis[hexaH] _ MIN[inter, 2*charHeight]; hAxis[hexaV] _ MIN[inter, charHeight]; IF (nAxis[analog]+nAxis[mark])#0 THEN hAxis[analog] _ (yActualSize-hAxis[hexaH]*nAxis[hexaH]-hAxis[hexaV]*cAxis[hexaV]) /(nAxis[analog]+nAxis[mark]) ELSE { IF nAxis[hexaH]#0.0 THEN hAxis[hexaH] _ (yActualSize-hAxis[hexaV]*cAxis[hexaV]) /nAxis[hexaH] ELSE hAxis[hexaV] _ inter; }; hAxis[mark] _ hAxis[analog]; }; PlotScaleX: PROC [plot: Plot, alpha: REAL] ~ { LockPlot[plot]; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO iAxisList.first.bounds.w _ alpha*iAxisList.first.bounds.w; ENDLOOP; UnlockPlot[plot]; }; DrawMag: PROC [viewer: Viewer, context: Imager.Context] ~ { zip: Draw2d.Zip; viewerData: ViewerData ~ NARROW[viewer.data]; x: REAL _ viewer.cw*viewerData.xOr; Imager.SetColor[context, tempColor]; zip _ Draw2d.GetZip[context]; Draw2d.Line[context, [x, 0.0], [x, viewer.ch], solid, zip]; x _ x+viewer.cw*viewerData.xScale; Draw2d.Line[context, [x, 0.0], [x, viewer.ch], solid, zip]; Draw2d.ReleaseZip[zip]; }; ClipVect: PROC [fix, mov: VEC, area: Rectangle] RETURNS [newPt: VEC] ~ { slope: REAL; newPt _ mov; IF fix.x~=mov.x THEN { slope _ (fix.y-mov.y)/(fix.x-mov.x); IF newPt.xarea.x+area.w THEN {newPt.x _ area.x+area.w; newPt.y _ slope*(newPt.x-fix.x) + fix.y}; }; IF fix.y~=mov.y THEN { slope _ (fix.x-mov.x)/(fix.y-mov.y); IF newPt.yarea.y+area.h THEN {newPt.y _ area.y+area.h; newPt.x _ (newPt.y-fix.y)*slope + fix.x}; }; }; IsPtInArea: PROC [pt: VEC, area: Rectangle] RETURNS [inside: BOOLEAN _ FALSE] ~ { IF pt.xarea.x+area.w THEN RETURN; IF pt.y>area.y+area.h THEN RETURN; inside _ TRUE; }; ShowRopeInv: PROC [x, y: REAL, rope: Rope.ROPE, context: Imager.Context, color, background: Imager.Color, font: Imager.Font] ~ { ropeSize: ImagerFont.Extents _ ImagerFont.RopeBoundingBox[font, rope]; Imager.SetColor[context, background]; Imager.MaskRectangle[context, [x-ropeSize.leftExtent, y-ropeSize.descent, ropeSize.leftExtent+ropeSize.rightExtent, ropeSize.descent+ropeSize.ascent]]; Imager.SetXY[context, [x, y]]; Imager.SetColor[context, color]; Imager.ShowRope[context, rope]; }; ShowRopeV: PROC[context: Imager.Context, rope: Rope.ROPE, pos: VEC, font: Imager.Font] ~ { charHeight: REAL _ ImagerFont.FontBoundingBox[font].ascent+1.0; IF rope=NIL THEN RETURN; FOR i: INT _ Rope.Length[rope]-1, i-1 UNTIL i=-1 DO Imager.SetXY[context, pos]; Imager.ShowChar[context, Rope.InlineFetch[rope, i]]; pos.y _ pos.y+ charHeight; ENDLOOP; }; ShowRope: PROC[x, y: REAL, rope: Rope.ROPE, context: Imager.Context, color: Imager.Color] ~ { IF rope=NIL THEN RETURN; Imager.SetColor[context, color]; Imager.SetXY[context, [x, y]]; Imager.ShowRope[context, rope]; }; DrawRopeV: PUBLIC PROC[pos: VEC, rope: Rope.ROPE, viewer: Viewer, color: Imager.Color, font: Imager.Font] ~ { DoDrawRope: PROC [viewer: Viewer, context: Imager.Context] ~ { Imager.SetFont[context, font]; Imager.SetColor[context, color]; ShowRopeV[context, rope, pos, font]; }; DrawInViewer[viewer, DoDrawRope, FALSE];-- ask the viewer procs to call you back }; DrawRope: PUBLIC PROC[pos: VEC, rope: Rope.ROPE, viewer: Viewer, color: Imager.Color, font: Imager.Font] ~ { DoDrawRope: PROC [viewer: Viewer, context: Imager.Context] ~ { Imager.SetFont[context, font]; ShowRope[pos.x, pos.y, rope, context, color]; }; DrawInViewer[viewer, DoDrawRope, FALSE];-- ask the viewer procs to call you back }; DrawRopeInv: PUBLIC PROC[pos: VEC, rope: Rope.ROPE, viewer: Viewer, color, background: Imager.Color, font: Imager.Font] ~ { DoDrawRope: PROC [viewer: Viewer, context: Imager.Context] ~ { Imager.SetFont[context, font]; ShowRopeInv[pos.x, pos.y, rope, context, color, background, font]; }; DrawInViewer[viewer, DoDrawRope, FALSE];-- ask the viewer procs to call you back }; Normalize: PROC [r: REAL] RETURNS [dec: REAL] ~ { neg: BOOL _ r<0.0; unsigned: REAL _ IF neg THEN -r ELSE r; base: REAL _ IF unsigned<1.0 THEN 10.0 ELSE 0.1; dec _ 1.0; IF r#0.0 THEN UNTIL unsigned>=1.0 AND unsigned<10.0 DO dec _ dec/base; unsigned _ unsigned*base; ENDLOOP; dec _ SELECT TRUE FROM unsigned<1.5 => dec, unsigned<3.5 => 2.0*dec, unsigned<7.5 => 5.0*dec, ENDCASE => 10.0*dec; IF neg THEN dec _ -dec; }; GetSelectedAxis: PUBLIC PROC[plot: Plot] RETURNS [axis: Axis] ~ { FOR iList: LIST OF REF ANY _ plot.selection, iList.rest UNTIL iList=NIL DO IF ISTYPE[iList.first, Axis] THEN { axis _ NARROW[iList.first]; EXIT; }; ENDLOOP; }; GetSelectedAxisList: PUBLIC PROC[plot: Plot] RETURNS [axisList: LIST OF Axis] ~ { axisList _ NIL; FOR iList: LIST OF REF ANY _ plot.selection, iList.rest UNTIL iList=NIL DO IF ISTYPE[iList.first, Axis] THEN axisList _ CONS[NARROW[iList.first, Axis], axisList]; ENDLOOP; }; SelectAxis: PUBLIC PROC [plot: Plot, axis: Axis] ~ { selectedAxis: Axis _ GetSelectedAxis[plot]; self : Viewer _ plot.private.viewer; charHeight: REAL _ ImagerFont.FontBoundingBox[currentFont].ascent+1.0; viewerData: ViewerData ~ NARROW[self.data]; xor, yor: REAL; hAxis: ARRAY DrawingStyle OF REAL _ ALL[0.0]; bblue: Imager.Color _ IF self.column=color THEN blue ELSE black; wwhite: Imager.Color _ IF self.column=color THEN grey ELSE white; IF axis=selectedAxis THEN RETURN; plot.selection _ IF axis=NIL THEN NIL ELSE LIST[axis]; xor _ xSpace; yor _ ySpace; hAxis _ SetHeights[plot, charHeight, IF viewerData.grid THEN self.ch-3*ySpace ELSE self.ch-2*ySpace]; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO thisAxis: Axis = iAxisList.first; inter: REAL _ AxisHeight[thisAxis, hAxis]; IF thisAxis=selectedAxis THEN DrawRopeInv[[xor+xAxisName, yor+yAxisName-charHeight], thisAxis.name, self, bblue, wwhite, currentFont]; IF thisAxis=axis THEN DrawRopeInv[[xor+xAxisName, yor+yAxisName-charHeight], thisAxis.name, self, wwhite, bblue, currentFont]; yor _ yor+inter; ENDLOOP; }; AxisHeight: PROC [axis: Axis, hAxis: ARRAY DrawingStyle OF REAL] RETURNS [h: REAL] ~ { h _ IF axis.style=hexaV THEN hAxis[hexaV]*(axis.maxChars+1.0) ELSE hAxis[axis.style] }; GetAxis: PROC [self: Viewer, x, y: REAL] RETURNS [axis: Axis, localx, localy: REAL] ~ { viewerData: ViewerData ~ NARROW[self.data]; charHeight: REAL _ ImagerFont.FontBoundingBox[currentFont].ascent+1.0; plot: Plot _ viewerData.plot; xor, yor, inter, t, v: REAL; hAxis: ARRAY DrawingStyle OF REAL _ ALL[0.0]; xor _ xSpace; yor _ ySpace; IF plot.axis=NIL THEN RETURN[NIL, 0.0, 0.0]; hAxis _ SetHeights[plot, charHeight, IF viewerData.grid THEN self.ch-3*ySpace ELSE self.ch-2*ySpace]; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO thisAxis: Axis = iAxisList.first; inter _ AxisHeight[thisAxis, hAxis]; IF y>=yor AND y˜>Kš œœœœœœ˜BKšœ:˜:Kšœ˜K˜K˜—š œœœœœœœœ˜NK™FK™BKšœœ˜ Kšœœ˜Kšœœ˜Kšœ˜Kš œ œœœœ˜;Kšœ˜Kšœ œœ˜Kšœœœ˜$Kšœ˜Kšœ>˜>Kšœ4˜4Kš œœœœœœ˜BKšœ˜Kšœ˜K˜K˜—š  œœœœœœœœ˜SKšœq™qKšœœœœ˜(Kšœœ#˜:K˜K˜—š œœœœœœœœ˜LKšœq™qKšœœ˜Kšœ œ˜šœ œ˜Kšœ>˜>Kšœ ˜Kšœ-˜-Kšœ˜—šœ˜Kšœ:˜:Kšœ!˜!šœœ˜KšœœA˜LKšœœX˜cKšœ œ[˜gšœ œ˜"Kšœ œ˜šœœ˜Kšœ#˜#Kšœ#˜#Kšœœœ,˜PK˜—K˜—Kšœ˜—Kšœ ˜ Kšœ˜Kšœ˜—K˜K˜—Kšœœ˜Kšœœœ˜K˜1Kšœ œ/˜?Kšœ œ œŸ6˜YKšœœ œŸ˜=Kš œœœœœ˜-Kšœ+˜+Kšœ2˜2Kšœœ˜Kšœœ˜Kšœœ˜Kšœ ˜Kšœ˜ Kšœ˜K™œšœœ˜K˜K˜ K˜K˜ K˜ K˜—šœ˜K˜Kšœ ˜ Kšœ˜Kšœ˜Kšœ˜K˜—šœ œ˜K™)Kšœ ˜ Kšœ.˜.K˜—K™Kšœ œœœ˜K™„Kšœ%œœœ ˜MK™PKšœœ œ;˜VK™gšœ1œ œ˜JKšœ!˜!Kšœœ˜Kšœ œœ˜K™Sšœ œ˜Kšœ œ˜šœ,œ œ˜Ešœœ˜"Kšœ œ˜Kšœ˜K˜—Kšœ˜—K˜—Kšœ œ˜K™šœ œ˜Kšœ/™/Kšœ˜Kšœ˜K™ZKšœ$˜$K™UKšœ˜Kšœ˜Kšœ"˜"Kšœ2˜2Kšœ˜šœ œ˜K™ šœœ˜&K˜!KšœN˜NK˜—šœœ˜&K˜!KšœM˜MK˜—K™4Kšœœl˜‰Kšœm˜qK˜—K™Išœ:œ œ˜TKšœ$˜$K™@šœ œ˜Kšœ œ˜šœ1œ œ˜Kšœœ˜$Kšœ œ˜Kšœ˜K˜—Kšœ˜—Kšœ œœ˜K˜—K™|Kšœ œ˜Kšœ*˜*Kšœ˜šœ:œ˜TKšœ˜Kšœ˜Kšœ˜Kšœ ˜—Kšœ˜Kšœœœ˜K™!Kšœ œZ˜lK™@KšœH˜HKšœœœœ˜VKšœ˜—K˜—K™@Kšœ˜Kšœ˜—K˜K˜—š   œœœœ2œœ˜‘š œœ.˜CK™Kšœœ˜Kšœœ˜"KšœK˜KKšœœœœ˜8KšœBŸ˜PKšœ%˜%Kšœ^˜^K™LKšœœ œ˜BK˜—Kšœœ˜:šœœ˜#K™-—Kšœ3œ˜:K˜K˜—š œœœ˜-Kšœ¡™¡š œœ˜+K™¿Kšœœ˜Kšœœ ˜K™;Kšœ+˜+Kšœ$˜$Kšœ&˜&Kšœ#˜#Kšœœœœ ˜VK˜K˜—Kšœœ˜:Kšœœ9˜PKšœ˜Kšœ=˜=KšœG˜GKšœ5˜5Kšœ,˜,Kšœ1˜1Kšœ˜Kšœ,˜,K˜K˜—š  œœœ˜(KšœO™OK˜%Kšœ ˜ K˜K™——™ šÏb œœ œœœœ2œ˜sKšœœ ˜ Kšœœ˜-Kšœ˜Kšœœ œœ˜FK˜šœ1œ œ˜JKšœ.˜.Kšœ.˜.KšœC˜CKšœC˜CKšœ˜—K˜Kšœ>œ˜DK˜K˜—š  œœ œœœœ!œœ˜qKšœœ ˜ Kšœœ˜-Kšœ#˜#KšœIœ˜NK˜K˜—š œœ œœœœ!œœ˜mKšœœ ˜ Kšœœ˜-Kšœ!˜!K˜K˜—š œœ œœœœ!œœ˜nKšœœ ˜ Kšœœ˜-Kšœ˜Kš œ œœ œœ˜8Kšœo˜oKšœ'˜'Kšœ$˜$K˜K˜—š  œœ œœœœ2œ˜qKšœœ ˜ Kšœœ˜-Kšœ˜Kšœœ˜ Kšœœœ˜"Kšœœœœ˜-Kšœœ˜"Kšœœœ˜+šœœ˜Kšœ,˜,Kšœ˜Kšœ>œ˜CKšœ˜—šœ˜š  œœ.˜=Kšœ˜Kšœœ˜-Kšœœ0˜7K•StartOfExpansion7[context: Imager.Context, color: ImagerColor.Color]šœ$˜$Kšœ˜Kšœ;˜;Kšœ,˜,Kšœ1˜1Kšœ;˜;Kšœ˜K˜—Kšœ œ˜'K˜—K˜K˜—š œ œ œœœœ2œ˜sKšœœ ˜ Kšœœ˜-Kšœ˜Kšœœ˜Kšœœœ˜"Kšœœ ˜$Kšœœ˜"Kšœœ˜6šœœ˜Kšœ˜Kšœ>œ˜CKšœ˜—Kšœ˜K˜—š œ œ œœœœ2œ˜sKšœœ ˜ Kšœœ˜-Kšœ˜Kšœœ˜Kšœœœ˜"Kšœœ ˜$Kšœœ˜"K˜šœ1œ œ˜JKšœ:˜:Kšœ˜—K˜Kšœ>œ˜CK˜K˜—š œ œ œœœœ!œœ˜uKšœœ ˜ Kšœœ˜-Kšœ˜Kš œ œœ œœ˜5KšœœœŸ˜1Kšœc˜cKšœ$˜$šœœ˜K˜šœ1œ œ˜JKšœF˜FKšœ\˜\Kšœ˜—K˜K˜—šœ˜K˜šœ1œ œ˜JKšœ\˜\KšœF˜FKšœ˜—K˜K˜—Kšœ%˜%Kšœ>œ˜DK˜——™š¡ œŸ˜DKšœO™Ošœ ˜šœ Ÿ˜%Kšœœ ˜+KšœIœ˜NKšœ˜—šœŸ˜,Kšœœœ,˜HKšœ˜——K˜K˜—š¡ œ˜(Kšœ Ÿ-™7Kšœ œ˜Kšœœ ˜+Kšœ˜Kšœ'œ&˜SKšœœ(Ÿ ˜Qšœ˜Kšœ$œ˜8Kšœ˜Kšœ˜šœœœœ˜(Kšœ œœ˜,Kšœ˜Kšœ'˜'šœ ˜šœ ˜ Kšœ>œ˜CKšœ˜—šœœ œœ˜$Kšœœœœ œœœœ˜›Kšœ6œœœ˜rKšœ˜Kšœ˜—šœ ˜ š œœ.˜;Kšœ˜Kšœœ˜-Kšœœ˜$K–7[context: Imager.Context, color: ImagerColor.Color]šœ$˜$Kšœ˜Kšœ=˜=Kšœ$˜$Kšœ=˜=Kšœ˜Kšœ;˜;Kšœ#˜#Kšœ=˜=Kšœ˜K˜—šœœœ˜=KšŸ%™%—Kšœ˜—˜Kšœ˜Kšœ˜—šœ˜Kšœ+˜+Kšœ,˜,Kšœ>œ˜CKšœ˜—šœ˜Kšœ+˜+Kšœ8˜8Kšœ>œ˜CKšœ˜—šœ˜Kšœ+˜+Kšœ,˜,Kšœ>œ˜CKšœ˜—šœ˜Kšœ ˜ Kšœ>œ˜CKšœ˜—šœ ˜ Kšœ+˜+Kšœ/˜/Kšœ>œ˜CKšœ˜—šœ˜Kšœ˜Kšœ>œ˜CKšœ˜—˜Kšœ˜šœ˜Kšœ!˜!Kšœ!˜!Kšœ!˜!Kšœ!˜!Kšœœ˜—Kšœ˜Kšœ>œ˜CKšœ˜—˜ Kšœ˜Kšœ>œ˜CKšœ˜—šœ ˜ Kšœ+˜+Kšœ)˜)Kšœœœ ˜:Kšœ4˜4Kšœ>œ˜CKšœ˜—šœ˜ Kšœœœ4˜NKšœ˜——K™K˜—K˜—K˜K˜—š¡ œ˜(Kšœœ5œ™XKšœœ ˜+Kšœ˜šœ˜Kšœœ˜ Kšœœ˜ Kšœœ˜ Kšœœ˜Kšœ˜—K˜K˜—š¡ œ˜*Kšœœ@™FKšœœ ˜+Kšœ˜K˜Kšœ œ˜Kšœ˜Kšœœ˜K˜K˜—š œ˜"Kš œœœœœœ™œ˜CKšœ˜K˜—š¡ œ#˜.KšœŸ™KšœŸ;œ Ÿ™oKšŸœ Ÿ™-Kšœœ ˜+Kšœ˜Kšœ œœœ˜šœ˜šœ ˜ K˜šœ1œ œ˜JKšœ^˜^Kšœ˜—Kšœ˜Kšœ˜—šœ ˜ K˜šœ1œ œ˜JKšœ^˜^Kšœ˜—Kšœ˜Kšœ˜—šœ ˜ KšœœJ˜TK˜šœ1œ œ˜JKšœ ˜ Kšœ˜—Kšœ˜Kšœ˜—šœ ˜ Kšœ ˜ Kšœ ˜ Kšœ'œœ˜5šœ1œ œ™JKšœœ™™Kšœœw™‚Kšœ™—šœ1œ œ˜JKšœœv˜€Kšœœ˜›Kšœ˜—Kš˜Kšœ˜—Kšœ˜—Kšœ>œ˜CKšœ˜K˜—š¡ œ˜(Kš œœœ œœ™6KšœBœ™GKšœ œ˜K˜K˜——™K™š œ œ˜.Kšœ˜Kšœœœœ˜"šœœ˜šœœ˜%Kšœ%˜%Kšœœ˜Kšœœ˜Kšœ)˜)Kšœœ˜—Kšœ%˜%Kšœ˜—K˜K˜—šÐbn œ œ$˜;Kšœ˜Kšœœœœ˜Kšœ˜Kšœœœ œ˜3šœ˜Kš œœ œœœ˜OKšœ œœœ˜0Kšœœ˜*K˜—Kšœ˜K˜K˜—š  œ œ˜4Kšœ˜Kšœ œœœ˜Kšœ˜Kšœœ0œ˜Sšœœ˜"Kšœ˜Kšœœœœ˜5Kšœ˜—Kšœ#˜#Kšœ˜K˜K˜—š œ œ$˜9Kšœ˜Kšœ˜K˜K˜—š¡ œ œ%˜=Kšœ˜Kšœ œœœ˜Kšœ˜šœ œœ˜šœ˜Kšœ ˜ Kšœ ˜ Kšœ˜—Kšœ˜Kšœ˜—Kšœ˜K˜K˜—š œ œœ˜@–Þ -- [graphs: PlotGraph.GraphList _ NIL, bounds: ImagerTransformation.Rectangle, name: ROPE _ NIL, style: PlotGraph.DrawingStyle _ PlotGraph.DrawingStyle[analog], maxChars: NAT _ 8, axisData: ARRAY XY OF PlotGraph.AxisData]šœ œ ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜—K˜K˜—š œ œ˜2Kšœ˜Kšœ!˜!Kšœ œœœ˜Kšœœœœ˜$šœ6œ œ˜PKšœ$˜$šœœœ˜,Kšœœ ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—Kšœœ˜Kšœ œœ˜.Kšœ˜—Kšœ˜šœœ˜Kšœ ˜ Kšœ˜K˜—šœ˜šœœ˜"Kšœ˜Kšœ œœœ˜0Kšœ˜—Kšœœœ%˜>Kšœ˜K˜—Kšœ˜K˜K˜—š œœœ+˜GKšœ˜Kšœœœœ˜Kšœœœœ˜Kšœ˜šœœœ˜$Kšœ#˜#Kšœ˜K˜—Kšœœ˜'Kšœ˜K˜K˜—š  œœœœœ˜HK™vKšœœ˜Kšœ0˜0KšœQ˜QKšœO˜OKšœ œ œœ˜'Kšœ˜K™šœ1œ œ˜JKšœ=˜=Kšœ%˜%Kšœ˜—Kšœ˜K˜——™ K™š  œœœ" œ˜aKšŸ™Kšœ œœœ˜1Kšœœœ$˜Ašœ˜KšœŸœ˜/Kšœ ˜ Kšœ˜Kšœ˜Kšœ˜—K˜K˜—š  œœ'œœ œœœœ ˜wKš œœœœœ˜4Kšœœ˜ Kšœ œœœ˜šœ1œ œ˜JKšœB˜BKšœ[˜[Kšœ˜—K™7KšœK˜KKšœœ˜(Kšœœ˜&Kšœœo˜”šœ˜KšœœE˜]Kšœ˜K˜—Kšœ˜K˜K˜—š  œœœ˜.K˜šœ1œ œ˜JKšœ:˜:Kšœ˜—Kšœ˜K˜K˜—š œœ.˜;Kšœ˜Kšœœ˜-Kšœœ˜#K–7[context: Imager.Context, color: ImagerColor.Color]šœ$˜$Kšœ˜Kšœ;˜;Kšœ"˜"Kšœ;˜;Kšœ˜K˜K˜—š  œœ œœ œ˜HK™lKšœœ˜ Kšœ ˜ šœœ˜K˜$Kšœœ=˜SKšœœD˜aK˜—šœœ˜K˜$Kšœœ=˜SKšœœD˜aK˜—K˜K˜—š   œœœœ œ˜QKšœ œœ˜Kšœ œœ˜Kšœœœ˜"Kšœœœ˜"Kšœ œ˜K˜K˜—š  œœœ œQ˜Kšœœ1˜FKšœ%˜%Kšœ—˜—Kšœ˜Kšœ ˜ Kšœ˜K˜K˜—š  œœ%œœ˜ZKšœ œ/˜?Kšœœœœ˜šœœœ˜3Kšœ˜Kšœ4˜4Kšœ˜Kšœ˜—K˜K˜—š œœœ œ1˜]Kšœœœœ˜Kšœ ˜ Kšœ˜Kšœ˜K˜K˜—š   œ œœ œœ3˜mš  œœœ$˜>Kšœ˜Kšœ ˜ Kšœ$˜$K˜—Kšœ!œŸ(˜PK˜K˜—š  œ œœ œœ3˜lš  œœœ$˜>Kšœ˜Kšœ-˜-K˜—Kšœ!œŸ(˜PK˜K˜—š   œ œœ œœ?˜{š  œœœ$˜>Kšœ˜KšœB˜BK˜—Kšœ!œŸ(˜PK˜K˜—š   œœœœœ˜1K™/Kšœœ ˜Kš œ œœœœ˜'Kš œœœœœ˜1Kšœ ˜ š œœœœ˜6Kšœ˜Kšœ˜Kšœ˜—šœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ ˜—Kšœœ ˜K˜K˜—š œ œ œ˜Ašœœœœœœœ˜Jšœœœ˜#Kšœœ˜Kš˜K˜—Kšœ˜—K˜K˜—š  œ œ œ œœ ˜QKšœ˜šœœœœœœœ˜JKš œœœ œœ˜WKšœ˜—K˜K˜—š  œ œ˜4Kšœ+˜+Kšœ$˜$Kšœ œ6˜FKšœœ ˜+Kšœ œ˜Kš œœœœœ˜-Kšœœœœ˜@Kšœœœœ˜AKšœœœ˜!Kšœœœœ˜6K˜ Kšœ ˜ Kšœ%œœœ˜ešœ1œ œ˜JKšœ!˜!Kšœœ˜*Kšœœi˜†Kšœœi˜~K˜Kšœ˜—K˜K˜—š  œœœœœœœ˜VKšœœœ"œ˜TK˜K˜—š  œœœœœ˜WKšœœ ˜+Kšœ œ6˜FKšœ˜Kšœœ˜Kš œœœœœ˜-K˜ Kšœ ˜ Kš œ œœœœ ˜,Kšœ#œœœ˜ešœ1œ œ˜JKšœ!˜!Kšœ$˜$šœœ œ˜ Kšœ8˜8Kšœœœ@œ˜eKšœ˜K˜—K˜Kšœ˜—K˜K˜——™š  œ œ˜7Kšœœ˜K˜K˜—š œ œ˜4Kšœœ˜-K˜K˜—š œ œœ˜Ešœœœœœœœ˜Jšœœœ˜$Kšœœ˜Kš˜K˜—Kšœ˜—K˜K˜—š  œ œœ œœ ˜UKšœ ˜šœœœœœœœ˜JKš œœœ œœ!˜[Kšœ˜—K˜K˜——™ š  œ œ$˜