<> <> <> <> <> <<>> 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 ~ { <<= PROC [self: Viewer, change: ModifyAction], ModifyAction: TYPE = {set, push, pop, kill}>> 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 ~ { <<~ PROC [self: Viewer] This will be called when the viewer is destroyed>> 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 ~ { <<= PROC [self: Viewer] RETURNS [adjusted: BOOL _ FALSE]>> <> 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> SelectGraph: PUBLIC PROC [plot: Plot, graph: Graph] ~ { plot.selection _ LIST[graph]; }; AddGraph: PUBLIC PROC [plot: Plot, graph: Graph] ~ { plot.selection _ CONS[graph, plot.selection]; }; GetSelectedGraph: PUBLIC PROC [plot: Plot] RETURNS [graph: Graph] ~ { FOR iList: LIST OF REF ANY _ plot.selection, iList.rest UNTIL iList=NIL DO IF ISTYPE[iList.first, Graph] THEN { graph _ NARROW[iList.first]; EXIT; }; ENDLOOP; }; GetSelectedGraphList: PUBLIC PROC [plot: Plot] RETURNS [graphList: LIST OF Graph] ~ { graphList _ NIL; FOR iList: LIST OF REF ANY _ plot.selection, iList.rest UNTIL iList=NIL DO IF ISTYPE[iList.first, Graph] THEN graphList _ CONS[NARROW[iList.first, Graph], graphList]; ENDLOOP; }; <> SelectPoint: PUBLIC PROC [plot: Plot, point: GraphPoint] ~ { plot.selection _ LIST[point]; }; AddPoint: PUBLIC PROC [plot: Plot, point: GraphPoint] ~ { plot.selection _ CONS[point, plot.selection]; }; GetSelectedPoint: PUBLIC PROC [plot: Plot] RETURNS [point: GraphPoint] ~ { FOR iList: LIST OF REF ANY _ plot.selection, iList.rest UNTIL iList=NIL DO IF ISTYPE[iList.first, GraphPoint] THEN { point _ NARROW[iList.first]; EXIT; }; ENDLOOP; }; GetSelectedPointList: PUBLIC PROC [plot: Plot] RETURNS [pointList: LIST OF GraphPoint] ~ { pointList _ NIL; FOR iList: LIST OF REF ANY _ plot.selection, iList.rest UNTIL iList=NIL DO IF ISTYPE[iList.first, GraphPoint] THEN pointList _ CONS[NARROW[iList.first, GraphPoint], pointList]; ENDLOOP; }; InsertPoint: PUBLIC PROC [graph: Graph, x, y: REAL] RETURNS [ok: BOOL _ TRUE] ~ { ok _ graph.class.insert#NIL; IF ok THEN graph.class.insert[graph, x, y]; }; DeletePoint: PUBLIC PROC [graph: Graph, x, y: REAL] RETURNS [ok: BOOL _ TRUE] ~ { ok _ graph.class.delete#NIL; IF ok THEN graph.class.delete[graph, x, y]; }; <> SelectText: PUBLIC PROC [plot: Plot, text: PlotText] ~ { plot.selection _ LIST[text]; }; AddText: PUBLIC PROC [plot: Plot, text: PlotText] ~ { plot.selection _ CONS[text, plot.selection]; }; GetSelectedText: PUBLIC PROC [plot: Plot] RETURNS [text: PlotText] ~ { FOR iList: LIST OF REF ANY _ plot.selection, iList.rest UNTIL iList=NIL DO IF ISTYPE[iList.first, PlotText] THEN { text _ NARROW[iList.first]; EXIT; }; ENDLOOP; }; GetSelectedTextList: PUBLIC PROC [plot: Plot] RETURNS [textList: LIST OF PlotText] ~ { textList _ NIL; FOR iList: LIST OF REF ANY _ plot.selection, iList.rest UNTIL iList=NIL DO IF ISTYPE[iList.first, PlotText] THEN textList _ CONS[NARROW[iList.first, PlotText], textList]; ENDLOOP; }; MoveText: PUBLIC PROC [plot: Plot, text: PlotText, position: VEC] ~ { text.bounds.x _ position.x; text.bounds.y _ position.y; }; RotateText: PUBLIC PROC [plot: Plot, text: PlotText, alpha: REAL] ~ { text.rotation _ text.rotation+alpha; }; DeleteText: PUBLIC PROC [plot: Plot, text: PlotText] ~ { IF plot.texts=NIL THEN RETURN; IF text=NIL THEN RETURN; IF plot.texts.first=text THEN { LockPlot[plot]; plot.texts _ plot.texts.rest; UnlockPlot[plot]; RETURN; }; FOR iTextList: LIST OF PlotText _ plot.texts, iTextList.rest UNTIL iTextList.rest=NIL DO IF iTextList.rest.first=text THEN { LockPlot[plot]; iTextList.rest _ iTextList.rest.rest; UnlockPlot[plot]; RETURN; }; ENDLOOP; }; <> <<>> black: PUBLIC Imager.Color _ Imager.black; white: PUBLIC Imager.Color _ Imager.white; grey: PUBLIC Imager.Color _ ImagerDitherContext.MakeSpecialColor[ImagerColor.ColorFromRGB[ [ R: 0.5, G: 0.5, B: 0.5 ] ], [190, null]]; <> red: PUBLIC Imager.Color _ ImagerColor.ColorFromRGB[ [ R: 1.0, G: 0.0, B: 0.0 ] ]; green: PUBLIC Imager.Color _ ImagerColor.ColorFromRGB[ [ R: 0.0, G: 1.0, B: 0.0 ] ]; blue: PUBLIC Imager.Color _ ImagerColor.ColorFromRGB[ [ R: 0.0, G: 0.0, B: 1.0 ] ]; puce: PUBLIC Imager.Color _ ImagerColor.ColorFromRGB[ [ R: 0.5, G: 0.2, B: 0.4 ] ]; tempColor: Imager.Color _ ImagerBackdoor.invert; defaultScalex: REAL _ 0.01; ipInColor: BOOLEAN _ FALSE; currentFont: Imager.Font _ ImagerFont.Find["Xerox/TiogaFonts/Gacha8"]; pressFont: Imager.Font _ ImagerFont.Scale[ImagerFont.Find["Xerox/PressFonts/Helvetica-brr"], 18.0]; pressScale: REAL _ 0.0002; pageSize: Rectangle _ [100.0, 100.0, 900.0, 1250.0]; fineStroke: REAL _ 1.0; coarseStroke: REAL _ 3.0; tinyTick: REAL _ 3.0; spaceOverTick: REAL _ 4.0; xSpace: REAL _ 2.0; ySpace: REAL _ 15.0; gridFactor: REAL _ 1.0; plotGPictureClass: ViewerClasses.ViewerClass _ NEW [ ViewerClasses.ViewerClassRec _ [ notify: NotifyProc, -- procedure to respond to input events (from TIP table) paint: PaintProc, -- procedure called when viewer contents must be repainted modify: ModifyProc, -- reports InputFocus changes destroy: DestroyProc, -- procedure to clean up when done get: GetProc, -- get the viewer contents i.e. the name of the selection scroll: VScrollProc, -- procedure to respond to vertical scroll bar hits hscroll: HScrollProc, -- procedure to respond to horizontal scroll bar hits adjust: AdjustProc, -- called when viewer size is changed <<-- Tip table (translates mouse events to commands)>> tipTable: TIPUser.InstantiateNewTIPTable["PlotGraph.TIP"], icon: Icons.NewIconFromFile["PlotGraph.icons", 3], cursor: crossHairsCircle -- cursor when mouse is in viewer ] ]; true: REF BOOLEAN _ NEW[BOOLEAN _ TRUE]; false: REF BOOLEAN _ NEW[BOOLEAN _ FALSE]; graphColorList: LIST OF Color _ LIST[black, red, green, blue, puce]; selection: LIST OF REF ANY _ NIL; xAxisName: REAL _ 2.0; yAxisName: REAL _ -1.0; ViewerOps.RegisterViewerClass[$PlotGViewer, plotGPictureClass];-- Register with viewers END.