<> <> <> <> <> <<>> DIRECTORY <> <> Imager USING [Context, SetColor, MaskRectangle, SetFont, MaskVector, Color, Font, SetXY, ShowChar, ShowRope, DoSaveAll, RotateT, Move], ImagerBackdoor USING [GetBounds, invert], ImagerColor USING [ColorFromRGB], <> ImagerDitherContext USING [MakeSpecialColor], ImagerFont USING [Scale, Find, RopeEscapement, FontBoundingBox], <> Draw2d, <> 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], <> PlotGraph; PlotGraphImpl: CEDAR MONITOR IMPORTS Imager, ImagerColor, <> ImagerDitherContext, <> ImagerFont, ImagerBackdoor, Draw2d, IO, Convert, Menus, <> ViewerOps, TIPUser, InputFocus, Rope, Real, Icons 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 upToDate: BOOL _ FALSE --the data is valid for painting ]; DrawPtData: TYPE ~ REF DrawPtDataRec; DrawPtDataRec: TYPE ~ RECORD [ window: Rectangle, plotOr: VEC, -- physical coordinates or: VEC, -- client coordinates prevPt: VEC, mult: VEC, context: Imager.Context, visible: BOOLEAN _ TRUE, firstPoint: BOOLEAN _ TRUE, viewerData: ViewerData ]; 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; }; WritePtV: PROC [x, y: REAL, data: REF ANY _ NIL, rope: ROPE _ NIL] RETURNS [quit: BOOL _ FALSE] ~ { <> <> <> x0, y0: REAL; n: INT; r: ROPE; ptData: DrawPtData ~ NARROW[data]; IF ~ptData.viewerData.upToDate THEN RETURN[TRUE]; n _ Real.InlineFix[y]; IF (Real.InlineFix[ptData.prevPt.y] = n) AND rope=NIL THEN RETURN; x0 _ (x+ptData.or.x)*ptData.mult.x+ptData.plotOr.x; IF x0ptData.window.x+ptData.window.w THEN RETURN; y0 _ ptData.plotOr.y; Imager.MaskVector[ptData.context, [x0, y0], [x0, y0+tinyTick]]; r _ IF rope~=NIL THEN rope ELSE Convert.RopeFromInt[n, 16, FALSE]; ShowRopeV[ptData.context, r, [x0 - 2.0, y0+spaceOverTick]]; ptData.prevPt _ [x0, y]; }; WritePtH: PROC [x, y: REAL, data: REF ANY _ NIL, rope: ROPE _ NIL] RETURNS [quit: BOOL _ FALSE] ~ { <> <> x0, y0: REAL; n: INT; r: ROPE; ptData: DrawPtData ~ NARROW[data]; IF ~ptData.viewerData.upToDate THEN RETURN[TRUE]; n _ Real.InlineFix[y]; IF (Real.InlineFix[ptData.prevPt.y] = n) AND rope=NIL THEN RETURN; x0 _ (x+ptData.or.x)*ptData.mult.x+ptData.plotOr.x; IF x0ptData.window.x+ptData.window.w THEN RETURN; y0 _ ptData.plotOr.y; Imager.MaskVector[ptData.context, [x0, y0], [x0, y0+tinyTick]]; Imager.SetXY[ptData.context, [x0 - 2.0, y0+spaceOverTick]]; r _ IF rope~=NIL THEN rope ELSE Convert.RopeFromInt[n, 16, FALSE]; Imager.ShowRope[ptData.context, r]; ptData.prevPt _ [x0, y]; }; DrawPt: PROC [x, y: REAL, data: REF ANY _ NIL, rope: ROPE _ NIL] RETURNS [quit: BOOL _ FALSE] ~ { <> pt: VEC; visible: BOOLEAN; ptData: DrawPtData ~ NARROW[data]; IF ~ptData.viewerData.upToDate THEN RETURN[TRUE]; IF ptData.firstPoint THEN { ptData.prevPt _ [(x+ptData.or.x)*ptData.mult.x+ptData.plotOr.x, (y+ptData.or.y)*ptData.mult.y+ptData.plotOr.y]; ptData.firstPoint _ FALSE; ptData.visible _ IsPtInArea[ptData.prevPt, ptData.window]; } ELSE { pt _ [(x+ptData.or.x)*ptData.mult.x+ptData.plotOr.x, (y+ptData.or.y)*ptData.mult.y+ptData.plotOr.y]; visible _ IsPtInArea[pt, ptData.window]; SELECT TRUE FROM visible AND ptData.visible => Imager.MaskVector[ptData.context, ptData.prevPt, pt]; visible AND ~ptData.visible => Imager.MaskVector[ptData.context, ClipVect[pt, ptData.prevPt, ptData.window], pt]; ~visible AND ptData.visible => Imager.MaskVector[ptData.context, ptData.prevPt, ClipVect[ptData.prevPt, pt, ptData.window]]; ~visible AND ~ptData.visible => { pt1, pt2: VEC; IF pt.y~=ptData.prevPt.y THEN { pt1 _ ClipVect[ptData.prevPt, pt, ptData.window]; pt2 _ ClipVect[pt, ptData.prevPt, ptData.window]; IF pt1.x~=pt2.x OR pt1.y~=pt2.y THEN Imager.MaskVector[ptData.context, pt1, pt2]; } }; ENDCASE; ptData.prevPt _ pt; ptData.visible _ visible; } }; 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]; }; RefreshScreen: PROC [plot: Plot, axis: AxisList _ NIL, graphs: GraphList _ NIL, within: Rectangle _ WorldRectangle, eraseFirst: BOOL _ FALSE] ~ { DoRefreshScreen: PROC [viewer: Viewer, context: Imager.Context] ~ { axisFound, graphFound, gridOK: BOOL; quit: BOOL _ FALSE; wwhite, rred, ggreen, bblue, ppuce: Imager.Color; axisChoice: BOOLEAN = ~(axis=NIL); --test only once the presence of an axis specification graphsChoice: BOOLEAN = ~(graphs=NIL); -- the same for graphs viewerData: ViewerData ~ NARROW[viewer.data]; xoffset, inter: REAL _ 0.0; hAxis: ARRAY DrawingStyle OF REAL _ ALL[0.0]; colorList: LIST OF Color; selectedAxis: Axis _ GetSelectedAxis[plot]; ptData: DrawPtData _ NEW[DrawPtDataRec _ [ window: WorldRectangle, plotOr: [xSpace, ySpace], or: [0.0, 0.0], prevPt: [0.0, 0.0], mult: [1.0, 1.0], context: context, firstPoint: TRUE, viewerData: viewerData ]]; <> viewerData.upToDate _ TRUE; IF viewer.column= color THEN { wwhite _ grey; rred _ red; ggreen _ green; bblue _ blue; ppuce _ puce; } ELSE { wwhite _ white; rred _ black; ggreen _ black; bblue _ black; ppuce _ black; }; Imager.SetFont[context, currentFont]; IF eraseFirst THEN { Imager.SetColor[context, wwhite]; Imager.MaskRectangle[context, ImagerBackdoor.GetBounds[context]]; -- fill screen <> FOR iTextList: LIST OF PlotText _ plot.texts, iTextList.rest UNTIL iTextList=NIL DO IF iTextList.first.wrt=NIL THEN { RotateAndShow: PROC ~ { <> Imager.SetColor[context, bblue]; Imager.SetXY[context, [ptData.plotOr.x+xScaled, ptData.plotOr.y+yScaled]]; Imager.Move[context]; Imager.RotateT[context, iTextList.first.rotation]; Imager.SetXY[context, [x, y]]; Imager.ShowRope[context, iTextList.first.contents]; }; x, y: REAL; <> xScaled: REAL _ iTextList.first.bounds.x*viewer.cw; yScaled: REAL _ iTextList.first.bounds.y*viewer.ch; x _ SELECT iTextList.first.justifyX FROM left => 0.0, center => MAX[0.0, (xScaled-ImagerFont.RopeEscapement[currentFont, iTextList.first.contents].x)*0.5], right => MAX[0.0, xScaled-ImagerFont.RopeEscapement[currentFont, iTextList.first.contents].x], ENDCASE => 0.0; <> y _ SELECT iTextList.first.justifyY FROM bottom => 0.0, center => MAX[0.0, (yScaled-ImagerFont.RopeEscapement[currentFont, iTextList.first.contents].y)*0.5], top => MAX[0.0, yScaled-ImagerFont.RopeEscapement[currentFont, iTextList.first.contents].y], ENDCASE => 0.0; <> Imager.DoSaveAll[context, RotateAndShow]; }; ENDLOOP; }; <> IF plot.axis=NIL THEN RETURN; <> gridOK _ viewerData.grid; hAxis _ SetHeights[plot]; <> IF gridOK AND eraseFirst THEN { x, y: REAL; unit: REAL _ NormalizeScaleAndOrigin[plot]; <> Imager.SetColor[context, ggreen]; x _ ptData.plotOr.x; y _ viewer.ch-2*ySpace; Imager.MaskVector[context, [x, y], [x+viewer.cw, y]]; y _ y-3.0; <> FOR i: INT IN [1..19] DO x _ ptData.plotOr.x+i*viewer.cw/20.0; Imager.MaskVector[context, [x, y], [x, y+6.0]]; ENDLOOP; <> ShowRope[ptData.plotOr.x+viewer.cw/20.0, y+12.0, IO.PutFR["%g", IO.real[unit]], context, bblue]; ShowRope[ptData.plotOr.x+4.0, y-12.0, IO.PutFR["%g", IO.real[plot.axis.first.bounds.x]], context, bblue] }; <> FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO <> IF axisChoice THEN { axisFound _ FALSE; FOR jAxisList: AxisList _ axis, jAxisList.rest UNTIL jAxisList=NIL DO IF jAxisList.first=iAxisList.first THEN { axisFound _ TRUE; EXIT; }; ENDLOOP; } ELSE axisFound _ TRUE; <> IF axisFound THEN { <> xoffset _ 2.0; colorList _ graphColorList; <> inter _ AxisHeight[iAxisList.first, hAxis]; <> ptData.or.x _ -iAxisList.first.bounds.x; ptData.or.y _ -iAxisList.first.bounds.y; ptData.mult.x _ viewer.cw/iAxisList.first.bounds.w; ptData.mult.y _ (inter-charHeight)/iAxisList.first.bounds.h; ptData.window _ [ptData.plotOr.x, ptData.plotOr.y, viewer.cw, inter]; IF eraseFirst THEN { <> IF iAxisList.first.axisData[X].visible THEN { Imager.SetColor[context, ggreen]; Imager.MaskVector[context, ptData.plotOr, [ptData.plotOr.x+viewer.cw, ptData.plotOr.y]]; }; IF iAxisList.first.axisData[Y].visible THEN { Imager.SetColor[context, ggreen]; Imager.MaskVector[context, ptData.plotOr, [ptData.plotOr.x, ptData.plotOr.y+inter]]; }; <> IF iAxisList.first=selectedAxis THEN ShowRopeInv[ptData.plotOr.x+xAxisName, ptData.plotOr.y+yAxisName, iAxisList.first.name, context, wwhite, bblue] ELSE ShowRopeInv[ptData.plotOr.x+xAxisName, ptData.plotOr.y+yAxisName, iAxisList.first.name, context, bblue, wwhite]; }; <> FOR iGraphList: GraphList _ iAxisList.first.graphs, iGraphList.rest UNTIL iGraphList=NIL DO <> IF graphsChoice THEN { graphFound _ FALSE; FOR jGraphList: GraphList _ graphs, jGraphList.rest UNTIL jGraphList=NIL DO IF jGraphList.first=iGraphList.first THEN { graphFound _ TRUE; EXIT; }; ENDLOOP; IF ~graphFound THEN LOOP; }; <> ptData.firstPoint _ TRUE; Imager.SetColor[context, colorList.first]; SELECT iAxisList.first.style FROM analog => quit _ iGraphList.first.class.enumerate[plot, iGraphList.first, within, DrawPt, ptData]; hexaH => quit _ iGraphList.first.class.enumerate[plot, iGraphList.first, within, WritePtH, ptData]; hexaV => quit _ iGraphList.first.class.enumerate[plot, iGraphList.first, within, WritePtV, ptData]; ENDCASE; IF quit THEN RETURN; <> IF eraseFirst THEN ShowRope[ptData.plotOr.x+xoffset, ptData.plotOr.y+inter*0.5, iGraphList.first.name, context, colorList.first]; <> xoffset _ xoffset+ImagerFont.RopeEscapement[currentFont, iGraphList.first.name].x+5.0; IF colorList.rest#NIL THEN colorList _ colorList.rest ELSE colorList _ graphColorList; ENDLOOP; }; <> ptData.plotOr.y _ ptData.plotOr.y+inter; ENDLOOP; <> <> IF ~viewerData.magOn AND eraseFirst THEN DrawMag[viewer, context]; }; viewerData: ViewerData _ NARROW[plot.private.viewer.data]; IF plot#viewerData.plot THEN ERROR; <> viewerData.upToDate _ FALSE; DrawInViewer[plot.private.viewer, DoRefreshScreen, FALSE]; }; ProduceIPMaster: PUBLIC PROC [plot: Plot] ~ { }; <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <<]];>> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <<};>> <> <> <> <> <<};>> <> <> <> <> <> <