DIRECTORY Imager USING [Context, SetColor, ColorOperator, MaskRectangle, SetFont, SetStrokeWidth, SetStrokeEnd, SetStrokeJoint, MaskVector, Color, Font, SetXY, ShowChar, ShowRope, DoSaveAll, RotateT, Move], ImagerBackdoor USING [GetBounds], ImagerColor USING [ColorFromRGB], ImagerColorOperator USING [RGBLinearColorModel], ImagerDitheredDevice USING [ColorFromSpecialPixel], ImagerFont USING [Scale, Find, RopeWidth, FontBoundingBox], ImagerInterpress USING [Ref, Create, DeclareColorOperator, DeclareFont, DoPage, Close], Draw2d, Menus USING [Menu, CreateMenu, AppendMenuEntry, ReplaceMenuEntry, CreateEntry, MouseButton, MenuEntry], ViewerClasses USING [Viewer, ViewerRec, ViewerClass, ViewerClassRec, NotifyProc, PaintProc, ModifyProc, DestroyProc, ScrollProc, HScrollProc, AdjustProc], 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 [FixI, Fix], MessageWindow USING [Append], PlotGraph; PlotGraphImpl: CEDAR MONITOR IMPORTS Imager, ImagerColor, ImagerColorOperator, ImagerDitheredDevice, ImagerInterpress, ImagerFont, ImagerBackdoor, Draw2d, IO, Convert, Menus, MessageWindow, 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; ViewerData: TYPE ~ REF ViewerDataRec; Color: TYPE ~ Imager.Color; ViewerDataRec: TYPE ~ RECORD [ plot: Plot, -- circular references... 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 xScale: REAL _ 0.125, --mag plot width / plot width xOr: REAL _ 0.0 --mag origin _ plot origin+ plot width*xOr ]; 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 ]; 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; viewerData: ViewerData _ NEW[ViewerDataRec]; 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]; n _ Real.Fix[y]; IF (Real.Fix[ptData.prevPt.y] = n) AND rope=NIL THEN RETURN; x0 _ (x+ptData.or.x)*ptData.mult.x+ptData.plotOr.x; IF x0<ptData.window.x THEN RETURN; IF x0>ptData.window.x+ptData.window.w THEN RETURN; y0 _ ptData.plotOr.y; Draw2d.Line[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]; n _ Real.Fix[y]; IF (Real.Fix[ptData.prevPt.y] = n) AND rope=NIL THEN RETURN; x0 _ (x+ptData.or.x)*ptData.mult.x+ptData.plotOr.x; IF x0<ptData.window.x THEN RETURN; IF x0>ptData.window.x+ptData.window.w THEN RETURN; y0 _ ptData.plotOr.y; Draw2d.Line[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.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 => Draw2d.Line[ptData.context, ptData.prevPt, pt]; visible AND ~ptData.visible => Draw2d.Line[ptData.context, ClipVect[pt, ptData.prevPt, ptData.window], pt]; ~visible AND ptData.visible => Draw2d.Line[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 Draw2d.Line[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] ~ { IF plot.private=NIL THEN CreatePlotViewer[plot]; IF plot.private.viewer.iconic THEN RETURN; IF NARROW[plot.private.viewer.data, ViewerData].frozen THEN RETURN; 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; 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 ]]; 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 Imager.SetColor[context, rred]; IF ~viewerData.magOn THEN DrawMag[viewer, context]; 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+iTextList.first.bounds.x*viewer.cw, ptData.plotOr.y+iTextList.first.bounds.y*viewer.ch]]; Imager.Move[context]; Imager.RotateT[context, iTextList.first.rotation]; Imager.SetXY[context, [x, y]]; Imager.ShowRope[context, iTextList.first.contents]; }; x, y: REAL; x _ SELECT iTextList.first.justifyX FROM left => ptData.plotOr.x+iTextList.first.bounds.x*viewer.cw, center => ptData.plotOr.x+(iTextList.first.bounds.x+MAX[0.0, (iTextList.first.bounds.w-ImagerFont.RopeWidth[currentFont, iTextList.first.contents].x)*0.5])*viewer.cw, right => ptData.plotOr.x+(iTextList.first.bounds.x+MAX[0.0, iTextList.first.bounds.w-ImagerFont.RopeWidth[currentFont, iTextList.first.contents].x])*viewer.cw, ENDCASE => 0.0; y _ SELECT iTextList.first.justifyY FROM bottom => ptData.plotOr.y+iTextList.first.bounds.y*viewer.ch, center => ptData.plotOr.y+(iTextList.first.bounds.y+ MAX[0.0, (iTextList.first.bounds.h-ImagerFont.RopeWidth[currentFont, iTextList.first.contents].y)*0.5])*viewer.ch, top => ptData.plotOr.y+(iTextList.first.bounds.y+MAX[0.0, iTextList.first.bounds.h-ImagerFont.RopeWidth[currentFont, iTextList.first.contents].y])*viewer.ch, 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; unit _ Normalize[plot.axis.first.bounds.w/20.0]; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO iAxisList.first.bounds.w _ 20.0*unit; ENDLOOP; Imager.SetColor[context, ggreen]; x _ ptData.plotOr.x; y _ ptData.plotOr.y+viewer.ch/2.0; Draw2d.Line[context, [x, y], [x+viewer.cw, y]]; y _ y-5.0; FOR i: INT IN [1..19] DO x _ ptData.plotOr.x+i*viewer.cw/20.0; Draw2d.Line[context, [x, y], [x, y+10.0]]; ENDLOOP; ShowRope[ptData.plotOr.x+viewer.cw/20.0, y+15.0, IO.PutFR["%g", IO.real[unit]], 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 _ IF iAxisList.first.style=hexaV THEN hAxis[hexaV]*(iAxisList.first.maxChars+1.0) ELSE hAxis[iAxisList.first.style]; 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]; Draw2d.Line[context, ptData.plotOr, [ptData.plotOr.x+viewer.cw, ptData.plotOr.y]]; }; IF iAxisList.first.axisData[Y].visible THEN { Imager.SetColor[context, ggreen]; Draw2d.Line[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 => [] _ iGraphList.first.class.enumerate[plot, iGraphList.first, within, DrawPt, ptData]; hexaH => [] _ iGraphList.first.class.enumerate[plot, iGraphList.first, within, WritePtH, ptData]; hexaV => [] _ iGraphList.first.class.enumerate[plot, iGraphList.first, within, WritePtV, ptData]; ENDCASE; IF eraseFirst THEN ShowRope[ptData.plotOr.x+xoffset, ptData.plotOr.y+inter*0.5, iGraphList.first.name, context, colorList.first]; xoffset _ xoffset+ImagerFont.RopeWidth[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 plot#NARROW[plot.private.viewer.data, ViewerData].plot THEN ERROR; DrawInViewer[plot.private.viewer, DoRefreshScreen, FALSE]; }; ProduceIPMaster: PUBLIC PROC [plot: Plot] ~ { DoPrint: PROC [context: Imager.Context] ~ { xoffset, inter: REAL; nAxis, hAxis, cAxis: ARRAY DrawingStyle OF REAL _ ALL[0.0]; colorList: LIST OF Color; ptData: DrawPtData _ NEW[DrawPtDataRec _ [ window: WorldRectangle, plotOr: [50.0, 100.0], or: [0.0, 0.0], prevPt: [0.0, 0.0], mult: [1.0, 1.0], context: context, firstPoint: TRUE ]]; Imager.SetStrokeEnd[context, round]; Imager.SetStrokeJoint[context, round]; Imager.SetFont[context, pressFont]; 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; ENDLOOP; inter _ pageSize.y/(nAxis[analog]+nAxis[hexaH]+cAxis[hexaV]); hAxis[hexaH] _ MIN[inter, charHeight+10.0]; hAxis[hexaV] _ MIN[inter, charHeight*1.15]; hAxis[analog] _ (pageSize.y -hAxis[hexaH]*nAxis[hexaH] -hAxis[hexaV]*cAxis[hexaV]) /nAxis[analog]; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO xoffset _ 0.0; colorList _ graphColorList; inter _ IF iAxisList.first.style=hexaV THEN hAxis[hexaV]*iAxisList.first.maxChars ELSE hAxis[iAxisList.first.style]; ptData.or.x _ -iAxisList.first.bounds.x; ptData.or.y _ -iAxisList.first.bounds.y; ptData.mult.x _ pageSize.x/iAxisList.first.bounds.w; ptData.mult.y _ (inter-10.0)/iAxisList.first.bounds.h; ptData.window _ [ptData.plotOr.x, ptData.plotOr.y, pageSize.x, inter]; IF iAxisList.first.axisData[X].visible THEN { Imager.SetStrokeWidth[context, fineStroke]; Imager.SetColor[context, green]; Imager.MaskVector[context, ptData.plotOr, [ptData.plotOr.x+1000.0, ptData.plotOr.y]]; }; IF iAxisList.first.axisData[Y].visible THEN { Imager.SetStrokeWidth[context, fineStroke]; Imager.SetColor[context, green]; Imager.MaskVector[context, ptData.plotOr, [ptData.plotOr.x, ptData.plotOr.y+inter]]; }; ShowRope[ptData.plotOr.x, ptData.plotOr.y+inter*0.1, iAxisList.first.name, context, blue]; FOR iGraphList: GraphList _ iAxisList.first.graphs, iGraphList.rest UNTIL iGraphList=NIL DO ptData.firstPoint _ TRUE; Imager.SetStrokeWidth[context, coarseStroke]; Imager.SetColor[context, colorList.first]; SELECT iAxisList.first.style FROM analog => [] _ iGraphList.first.class.enumerate[plot, iGraphList.first, within, DrawPt, ptData]; hexaH => [] _ iGraphList.first.class.enumerate[plot, iGraphList.first, within, WritePtH, ptData]; hexaV => [] _ iGraphList.first.class.enumerate[plot, iGraphList.first, within, WritePtV, ptData]; ENDCASE; ShowRope[ptData.plotOr.x+xoffset, ptData.plotOr.y+inter*0.5, iGraphList.first.name, context, colorList.first]; xoffset _ xoffset+ImagerFont.RopeWidth[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; }; 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 ~ ImagerColorOperator.RGBLinearColorModel[255]; ImagerInterpress.DeclareColorOperator[ip, rgbLinear]; ImagerInterpress.DeclareFont[ip, pressFont]; ImagerInterpress.DoPage[ip, DoPrint, pressScale]; ImagerInterpress.Close[ip]; MessageWindow.Append[Rope.Cat[fileName, " created"],TRUE]; }; 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]; liasonState: BOOLEAN _ NARROW[clientData, REF BOOLEAN]^; Menus.ReplaceMenuEntry[viewer.menu, liasonEntry[liasonState], 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] ~ { Imager.SetColor[context, IF viewer.column=color THEN grey ELSE white]; DrawMag[viewer, context]; viewerData.xScale _ viewerData.xScale*alpha; Imager.SetColor[context, IF viewer.column=color THEN red ELSE black]; DrawMag[viewer, context]; }; 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, magEntry[magState], 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.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] ELSE DrawRope[[x, y], IO.PutFR["t:%d", IO.real[t]], self, IF self.column= color THEN red ELSE black]; RETURN; }; $MoveMag => { MoveMag: PROC [viewer: Viewer, context: Imager.Context] ~ { Imager.SetColor[context, IF viewer.column=color THEN grey ELSE white]; DrawMag[viewer, context]; viewerData.xOr _ x/viewer.cw; Imager.SetColor[context, IF viewer.column=color THEN red ELSE black]; DrawMag[viewer, context]; }; 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] }; 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; }; VScrollProc: ViewerClasses.ScrollProc ~ { viewerData: ViewerData ~ NARROW[self.data]; plot: Plot _ viewerData.plot; 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 => { RETURN; }; 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.FixI[100.0*(plot.upperBounds.y-iAxisList.first.bounds.y-iAxisList.first.bounds.h) /(plot.upperBounds.y-plot.lowerBounds.y)]]; bottom _ MIN[bottom, Real.FixI[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; 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 => { RETURN; }; 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.FixI[100.0*(iAxisList.first.bounds.x-plot.lowerBounds.x) /(plot.upperBounds.x-plot.lowerBounds.x)]]; right _ MIN[right, Real.FixI[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 => NULL; 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 ]]; }; 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] RETURNS [hAxis: ARRAY DrawingStyle OF REAL _ ALL[0.0]] ~ { viewer: Viewer ~ plot.private.viewer; nAxis, cAxis: ARRAY DrawingStyle OF REAL _ ALL[0.0]; inter: REAL; 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 _ (viewer.ch-2*ySpace) /(nAxis[analog]+nAxis[hexaH]+cAxis[hexaV]); hAxis[hexaH] _ MIN[inter, charHeight+10.0]; hAxis[hexaV] _ MIN[inter, charHeight]; IF nAxis[analog]#0 THEN hAxis[analog] _ (viewer.ch-2*ySpace-hAxis[hexaH]*nAxis[hexaH] -hAxis[hexaV]*cAxis[hexaV]) /nAxis[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] ~ { viewerData: ViewerData ~ NARROW[viewer.data]; x: REAL _ viewer.cw*viewerData.xOr; Draw2d.Line[context, [x, 0.0], [x, viewer.ch]]; x _ x+viewer.cw*viewerData.xScale; Draw2d.Line[context, [x, 0.0], [x, viewer.ch]]; }; 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.x<area.x THEN {newPt.x _ area.x; newPt.y _ slope*(newPt.x-fix.x) + fix.y}; IF newPt.x>area.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.y<area.y THEN {newPt.y _ area.y; newPt.x _ (newPt.y-fix.y)*slope + fix.x}; IF newPt.y>area.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.x<area.x THEN RETURN; IF pt.y<area.y THEN RETURN; IF pt.x>area.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] ~ { Imager.SetColor[context, background]; Imager.MaskRectangle[context, [x, y-1.0, ImagerFont.RopeWidth[currentFont, rope].x, charHeight+2.0]]; Imager.SetXY[context, [x, y]]; Imager.SetColor[context, color]; Imager.ShowRope[context, rope]; }; ShowRopeV: PROC[context: Imager.Context, rope: Rope.ROPE, pos: VEC] ~ { 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] ~ { DoDrawRope: PROC [viewer: Viewer, context: Imager.Context] ~ { Imager.SetColor[context, color]; ShowRopeV[context, rope, pos]; }; 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] ~ { DoDrawRope: PROC [viewer: Viewer, context: Imager.Context] ~ { Imager.SetFont[context, currentFont]; 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] ~ { DoDrawRope: PROC [viewer: Viewer, context: Imager.Context] ~ { Imager.SetFont[context, currentFont]; ShowRopeInv[pos.x, pos.y, rope, context, color, background]; }; 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; xor, yor, inter: 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; plot.selection _ LIST[axis]; xor _ xSpace; yor _ ySpace; hAxis _ SetHeights[plot]; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO inter _ IF iAxisList.first.style=hexaV THEN hAxis[hexaV]*(iAxisList.first.maxChars+1.0) ELSE hAxis[iAxisList.first.style]; IF iAxisList.first=selectedAxis THEN DrawRopeInv[[xor+xAxisName, yor+yAxisName], iAxisList.first.name, self, bblue, wwhite]; IF iAxisList.first=axis THEN DrawRopeInv[[xor+xAxisName, yor+yAxisName], iAxisList.first.name, self, wwhite, bblue]; yor _ yor+inter; ENDLOOP; }; GetAxis: PROC [self: Viewer, x, y: REAL] RETURNS [axis: Axis, localx, localy: REAL] ~ { viewerData: ViewerData ~ NARROW[self.data]; plot: Plot _ viewerData.plot; xor, yor, inter, t, v: REAL; hAxis: ARRAY DrawingStyle OF REAL _ ALL[0.0]; xor _ xSpace; yor _ ySpace; hAxis _ SetHeights[plot]; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO inter _ IF iAxisList.first.style=hexaV THEN hAxis[hexaV]*iAxisList.first.maxChars ELSE hAxis[iAxisList.first.style]; IF y>=yor AND y<yor+inter THEN { t _ iAxisList.first.bounds.x+iAxisList.first.bounds.w*(x-xor)/self.cw; v _ IF inter#charHeight THEN iAxisList.first.bounds.y+iAxisList.first.bounds.h*(y-yor)/(inter-charHeight) ELSE 0.0; RETURN[iAxisList.first, t, v]; }; yor _ yor+inter; ENDLOOP; }; GetAxisBox: PROC [self: Viewer, axis: Axis] RETURNS [box: Rectangle] ~ { viewerData: ViewerData ~ NARROW[self.data]; plot: Plot _ viewerData.plot; xor, yor, inter: REAL; hAxis: ARRAY DrawingStyle OF REAL _ ALL[0.0]; xor _ xSpace; yor _ ySpace; hAxis _ SetHeights[plot]; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO inter _ IF iAxisList.first.style=hexaV THEN hAxis[hexaV]*iAxisList.first.maxChars ELSE hAxis[iAxisList.first.style]; IF iAxisList.first=axis THEN { RETURN[[xor, yor, self.cw, inter]]; }; yor _ yor+inter; ENDLOOP; }; 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; }; black: PUBLIC Imager.Color _ ImagerColor.ColorFromRGB[ [ R: 0.0, G: 0.0, B: 0.0 ] ]; white: PUBLIC Imager.Color _ ImagerColor.ColorFromRGB[ [ R: 1.0, G: 1.0, B: 1.0 ] ]; grey: PUBLIC Imager.Color _ ImagerDitheredDevice.ColorFromSpecialPixel[[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 ] ]; defaultScalex: REAL _ 0.01; 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: VEC _ [1000.0, 1250.0]; fineStroke: REAL _ 1.0; coarseStroke: REAL _ 3.0; charHeight: REAL _ ImagerFont.FontBoundingBox[currentFont].ascent+1.0; tinyTick: REAL _ 3.0; spaceOverTick: REAL _ 4.0; xSpace: REAL _ 2.0; ySpace: REAL _ 12.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 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 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]; 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" ]; magEntry: ARRAY BOOLEAN OF Menus.MenuEntry _ [magOffEntry, magOnEntry]; liasonEntry: ARRAY BOOLEAN OF Menus.MenuEntry _ [frozenEntry, activeEntry]; graphColorList: LIST OF Color _ LIST[black, red, green, blue, puce]; selection: LIST OF REF ANY _ NIL; xAxisName: REAL _ 2.0; yAxisName: REAL _ -charHeight-2.0; ViewerOps.RegisterViewerClass[$PlotGViewer, plotGPictureClass];-- Register with viewers END. ��Ò��PlotGraphImpl.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Christian LeCocq September 30, 1986 8:54:08 am PDT Barth, August 25, 1986 12:32:13 pm PDT PlotGraph is a package which claimed to be of general interest, but was tailored to fit the needs of the time simulators from one hand, and the users with oscilloscope habits on the other hand. A lot of handy feature were sacrificed in the name of speed of display. Imager Viewers others Type Definitions Plot management Builds the graphic viewer, and establish the data structures. creates a graphic viewer and the menu buttons copy the main data structure in a twin pointing at the same axisList write "rope" if any, or the hexa value of y, at location approx [x, 0] when rope is NIL y is written only if it is # than the previous y. the value is written top down. write "rope" if any, or the hexa value of y, at location approx [x, 0] the value is written as we are used to in Indo-European languages. draw the vector from the previous pt to the current on with clipping in ptData.window, except of course the first point. If the viewer is on the B&W display we will draw black vectors only, due to the poor readability of the patterns of bits simulating color on the B&W screen. If the magnifier is not on, just show the limits (2 vertical vectors) of it. Show the global texts on the full screen. called through Imager.DoSaveAll to avoid having to restore the current origin and rotation (actually absence of rotation). make the appropriate x translation according to x justification make the appropriate y translation according to y justification then do it and forget these temporary transformations. Draw the curves Verify the coherence of the axis width along the plot to enable the grid and count the number and size of the various axis flavours. Draw the grid, after adjusting the x scale to a "round decimal" value if needed. unit is 1, 2 or 5 times a power of ten in client x coordinates, which are supposed to be coherent all over the graphs. scale adequately the axis. Draw a mid screen horizontal green (or black) vector and mark 20 units by drawing well... let's see... 19 ticks I guess. write the unit value in fixed or floating notation depending on the size of it, over the first tick. Draw the various axis after the other stuff so that the significant lines will overwrite anything else. if a specific axis list was given let's check it, else (which is common) lets draw. draw the axis and its furniture xoffset and colorList for multi graphs per axis If this axis contains vertical numbers, then scale it according to the max number of chars prepair the COMMON (COMMON is a trade mark of FORTRAN and sons) for the drawPt procs. Draw both local axis, if needed. then display the name of the axis under its baseline we are not so far from what we really wanted to do : display [x, y] pairs check if a graph list was specified for the matching of this one here we are ! set the appropriate color and ask the client for data, sending him the draw proc appropriate for its religion. name the graph in the same color. prepair the new color and the new name offset for the next graph even if this axis was not shown, it is worth update the y origin some data structure went corrupted somehow... Creates a file named "///Temp/PlotGraph/nameOfThisPlot.interpress" which contains an InterPress master reproducing more or less the screen when it was buttonned. essentially the same structure as for the screen except for the selective tests, for a lot of "suitably adjusted" constants to fit a page, and of course the fonts and the width of the stroke. Interpress specific stuff: adjusting the shape of the lines the client way to kill the viewer, and then empty the plot. see the DestroyProc Menu Actions ViewerClasses Definitions PROC [self: Viewer, context: Imager.Context, whatChanged: REF ANY, clear: BOOL] NotifyProc ~ PROC[self: Viewer, input: LIST OF REF ANY] ask the viewer procs to call you back = PROC [self: Viewer, change: ModifyAction], ModifyAction: TYPE = {set, push, pop, kill} ~ PROC [self: Viewer] This will be called when the viewer is destroyed Acts on scrollbar mouse hits ScrollProc ~ PROC[self: Viewer, op: ScrollOp, amount: INTEGER] RETURNS[top, bottom: INTEGER _ LAST[INTEGER]]; ScrollOp: TYPE = {query, up, down, thumb} FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO iAxisList.first.bounds.y _ amount*0.01*(plot.clientBounds.h - plot.clientBounds.y) + plot.clientBounds.y; ENDLOOP; Acts on hscrollbar mouse hits HScrollProc ~ PROC[self: Viewer, op: HScrollOp, amount: INTEGER] RETURNS[left, right: INTEGER _ LAST[INTEGER]]; HScrollOp: TYPE = {query, left, right, thumb} FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO iAxisList.first.bounds.x _ amount*0.01*(plot.clientBounds.w - plot.clientBounds.x) + plot.clientBounds.x; ENDLOOP; FOR iAxisList: AxisList _ plot.axis, iAxisList.rest UNTIL iAxisList=NIL DO left _ MAX[left, Real.FixI[100.0*(plot.upperBounds.x-iAxisList.first.bounds.x-iAxisList.first.bounds.w) /(plot.upperBounds.x-plot.lowerBounds.x)]]; right _ MIN[right, Real.FixI[100.0*(plot.upperBounds.x-iAxisList.first.bounds.x) /(plot.upperBounds.x-plot.lowerBounds.x)]]; ENDLOOP; = PROC [self: Viewer] RETURNS [adjusted: BOOL _ FALSE] ViewerBLT will assume that a full repaint is required if adjusted=TRUE. Data Structure Modifications Utilities Pass procedure to PaintProc IF iAxisList.first.bounds.w~=plot.axis.first.bounds.w THEN gridOK _ FALSE; compute the unitary heigth of each kind for this viewer fix is inside area, mov outside. newPt is the intersection of the vector [fix, mov] and the area boundaries. rounds r to the closest "round decimal number" Graphs Graph Points Texts Initialization -- Tip table (translates mouse events to commands) Ê2©��˜�codešœ™Kšœ Ïmœ1™<K™2K™&KšœŠ™Š—K™�šÏk ˜ K™Kšœžœ¸˜ÄKšœžœ ˜!Kšœžœ˜!Kšœžœ˜0Kšœžœ˜3Kšœžœ*˜;KšœžœA˜WK˜K™Kšœžœ\˜gKšœžœ‡˜šKšœ žœA˜PKšœžœ+˜8Kšœžœ˜Kšœžœ ˜0K™Kšœžœ˜Kšœžœ˜Kšœžœžœ˜Kšœžœžœ˜,Kšœžœ ˜Kšœžœ ˜šœ ˜ K˜�——K˜�KšÐbl œžœž˜šžœ˜Kšœ˜Kšœ˜K˜K˜Kšœ˜Kšœ˜Kšœ˜K˜Kšœ˜K˜Kšœ˜Kšœ˜Kšœ ˜ Kšœ˜K˜K˜Kšœ˜Kšœ˜—K˜�Kšžœ ˜Kšœž œ˜head™Kšžœžœžœ˜Kšœ žœžœžœ˜Kšœ žœžœžœ˜Kšœžœžœžœ˜ Kšœžœ˜$Kšœžœžœ˜%Kšœžœ˜šœžœžœ˜Kšœ Ïc˜&Kšœžœžœ 8˜PKšœžœžœ F˜]Kšœžœžœ ˜-Kšœžœ ˜3Kšœžœ *˜:K˜—Kšœžœžœ˜%šœžœžœ˜K˜Kšœžœ ˜%Kšœžœ ˜Kšœžœ˜Kšœžœ˜ Kšœ˜Kšœ žœžœ˜Kšœžœž˜K˜——™šÏn œžœžœžœžœžœ˜CK™=Kšœžœ ˜Kšœžœžœ ˜Kšœžœ˜(Kšœ˜K˜K˜�—š¡œžœ˜'K™-Kšœ˜Kšœžœ˜,Kšœžœ˜(Kšœ˜Kšœ˜Kšœ5˜5Kšœ6˜6Kšœ4˜4Kšœ4˜4Kšœ6˜6Kšœ5˜5Kšœ7˜7Kšœ5˜5šœ-˜-Kšœ˜šœ˜Kšœ˜K˜Kšœžœ˜ K˜ Kšœž˜Kšœ žœ˜Kšœžœ˜ Kšœžœ ˜1Kšœ˜K˜—K˜—K˜K˜�—š¡œžœ žœ˜=K™DKšœ˜Kšœžœ˜Kšœžœžœ˜1Kšœžœžœ˜Kšœ˜šžœ0žœžœž˜IKšœžœžœ˜6Kšœ˜Kšžœ˜—Kšœ˜K˜K˜�—š¡œžœžœ˜(Kšžœžœžœ˜Kšž œ˜ K˜K˜�—š¡œžœžœžœ˜+Kšžœžœžœ˜Kšžœžœžœžœ˜Kšžœžœžœžœ˜ Kšžœžœžœžœ˜AKš žœžœžœžœ +˜LKšœžœ˜Kšœ˜K˜�—š¡ œžœžœžœ˜,Kšžœžœžœ˜Kšžœžœžœžœ˜Kšžœžœžœžœ˜ Kšœžœ˜Kšž œ˜ Kšœ˜K˜�—š¡œžœžœžœžœžœžœžœžœ˜cK™FK™BK™Kšœžœ˜ Kšœžœ˜Kšœžœ˜Kšœžœ˜"Kšœ˜Kš žœ!žœžœžœžœ˜<Kšœ3˜3Kšžœžœžœ˜"Kšžœ$žœžœ˜2Kšœ˜Kšœ9˜9Kšœžœžœžœžœžœ˜BKšœ;˜;Kšœ˜K˜K˜�—š¡œžœžœžœžœžœžœžœžœ˜cK™FK™BKšœžœ˜ Kšœžœ˜Kšœžœ˜Kšœžœ˜"Kšœ˜Kš žœ!žœžœžœžœ˜<Kšœ3˜3Kšžœžœžœ˜"Kšžœ$žœžœ˜2Kšœ˜Kšœ9˜9Kšœ;˜;Kšœžœžœžœžœžœ˜BKšœ#˜#Kšœ˜K˜K˜�—š¡œžœžœžœžœžœžœžœžœ˜aKšœx™xKšœžœ˜Kšœ žœ˜Kšœžœ˜"šžœžœ˜Kšœo˜oKšœž˜Kšœ:˜:Kšœ˜—šžœ˜Kšœd˜dKšœ(˜(šžœžœž˜KšœžœB˜MKšœžœ`˜kKšœ žœj˜všœ žœ˜!Kšœ žœ˜šžœžœ˜Kšœ1˜1Kšœ1˜1Kšžœžœžœ'˜KK˜—K˜—Kšžœ˜—Kšœ˜Kšœ˜Kšœ˜—K˜K˜�—š¡œžœžœžœžœ2žœžœ˜–Kšžœžœžœ˜0Kšžœžœžœ˜*Kšžœžœ.žœžœ˜CKšœ6˜6K˜K˜�—š¡ œžœžœžœ2žœžœ˜‘š¡œžœ.˜CKšœžœ˜$K˜1Kšœžœ žœ 6˜YKšœžœžœ ˜=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˜—Kšœ%˜%šžœžœ˜Kšœ!˜!KšœB ˜PK™LKšœ˜Kšžœžœ˜3K™)šžœžœžœ&žœžœž˜Sšžœžœžœ˜!š¡ œžœ˜Kšœz™zKšœ ˜ Kšœ€˜€Kšœ˜Kšœ2˜2Kšœ˜Kšœ3˜3K˜—Kšœžœ˜K™?šœžœž˜(Kšœ;˜;Kšœ¦˜¦KšœŸ˜ŸKšžœ˜—K™?šœžœžœž˜(Kšœ=˜=Kšœ4žœo˜§Kšœ1žœi˜Kšžœ˜—K™6Kšœ)˜)K˜—Kšžœ˜—K˜—K™Kšžœžœžœžœ˜K™„Kšœ˜Kšœ˜K™Pšžœžœžœ˜Kšœžœ˜Kšœžœ˜K™vKšœ0˜0K™šžœ1žœžœž˜JKšœ%˜%Kšžœ˜—K™4Kšœ!˜!Kšœ˜Kšœ"˜"Kšœ/˜/K˜ K™Cšžœžœžœ ž˜Kšœ%˜%Kšœ*˜*Kšžœ˜—K™dKšœ1žœ žœ˜_K˜—K™gšžœ1žœžœž˜JK™Sšžœžœ˜Kšœžœ˜šžœ,žœžœž˜Ešžœ žœ˜)Kšœžœ˜Kšžœ˜K˜—Kšžœ˜—K˜—Kšžœ žœ˜K™šžœžœ˜Kšœ/™/Kšœ˜Kšœ˜K™ZKšœžœžœ-žœ˜zK™UKšœ(˜(Kšœ(˜(Kšœ3˜3Kšœ<˜<KšœE˜Ešžœžœ˜K™ šžœ$žœ˜-K˜!KšœR˜RK˜—šžœ$žœ˜-K˜!KšœN˜NK˜—K™4Kšžœžœp˜”Kšžœq˜uK˜—K™IšžœAžœžœž˜[K™@šžœ žœ˜Kšœ žœ˜šžœ1žœžœž˜Kšžœ"žœ˜+Kšœ žœ˜Kšžœ˜K˜—Kšžœ˜—Kšžœ žœžœ˜K˜—K™|Kšœžœ˜Kšœ*˜*šžœž˜!Kšœ`˜`Kšœb˜bKšœb˜bKšžœ˜—K™!Kšžœžœo˜K™@KšœQ˜QKšžœžœžœžœ˜VKšžœ˜—K˜—K™@Kšœ(˜(Kšžœ˜—K˜—šžœžœ,žœž˜EK™-—Kšœ3žœ˜:K˜K˜�—š¡œžœžœ˜-Kšœ(Ñopqœk™¡š¡œžœ˜+K™¿Kšœžœ˜Kš œžœžœžœžœ˜;Kšœžœžœ˜šœžœ˜*K˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœž˜Kšœ˜—K™;Kšœ$˜$Kšœ&˜&Kšœ#˜#šžœ1žœžœž˜JKšœB˜BKšœW˜WKšžœ˜—Kšœ=˜=Kšœžœ˜+Kšœžœ˜+Kšœb˜bšžœ1žœžœž˜JKšœ˜Kšœ˜Kšœžœžœ'žœ˜tKšœ(˜(Kšœ(˜(Kšœ4˜4Kšœ6˜6KšœF˜Fšžœ$žœ˜-Kšœ+˜+Kšœ ˜ KšœU˜UK˜—šžœ$žœ˜-Kšœ+˜+K˜ KšœT˜TK˜—KšœZ˜ZšžœAžœžœž˜[Kšœžœ˜Kšœ-˜-Kšœ*˜*šžœž˜!Kšœ`˜`Kšœb˜bKšœb˜bKšžœ˜—Kšœn˜nKšœQ˜QKšžœžœžœžœ˜VKšžœ˜—Kšœ(˜(Kšžœ˜—K˜—Kšœžœ9ž˜PKšœ˜Kšœ=˜=KšœO˜OKšœ5˜5Kšœ,˜,Kšœ1˜1Kšœ˜Kšœ4žœ˜: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š œ žœžœ žœžœ˜8KšœY˜YKšœ'˜'Kšœ$˜$K˜K˜�—š¡ œžœ žœžœžœžœ2žœ˜qKšœžœ ˜ Kšœžœ˜-Kšœ˜Kšœžœ˜Kšžœžœžœ˜"Kšœžœžœžœ˜-Kšžœžœ˜"Kšžœžœžœ˜+šžœžœ˜Kšœ,˜,Kšœ˜Kšœ>žœ˜CKšœ˜—šžœ˜š¡ œžœ.˜=Kšœžœžœžœ˜FKšœ˜Kšœ,˜,Kšœžœžœžœ˜EKšœ˜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šœM˜MKšœ$˜$šžœžœ˜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žœžœžœ˜eKšžœ˜Kšœ˜—šœ ˜ š¡œžœ.˜;Kšœžœžœžœ˜FKšœ˜Kšœ˜Kšœžœžœžœ˜EKšœ˜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šžœžœžœ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šœ ™Kšœ `™mKš )™)Kšœžœ˜+Kšœ˜šžœž˜šœ˜K˜šžœ1žœžœž˜JKšœ^˜^Kšžœ˜—Kšœ˜Kšœ˜—šœ ˜ K˜šžœ1žœžœž˜JKšœ^˜^Kšžœ˜—Kšœ˜Kšœ˜—šœ ˜ Kšžœ˜šžœ1žœžœž™JKšœi™iKšžœ™—Kšœ˜—šœ ˜ K˜K˜ Kšžœ'žœžœ˜5šžœ1žœžœž˜JKšœžœˆ˜‘Kšœ žœr˜~Kšžœ˜—Kšžœ˜Kšœ˜—Kšžœ˜—Kšœ>žœ˜CKšœ˜K˜�—š£œ#˜.Kšœ ™Kšœ ;œ ™oKš œ ™-Kšœžœ˜+Kšœ˜šžœž˜šœ ˜ K˜šžœ1žœžœž˜JKšœ^˜^Kšžœ˜—Kšœ˜Kšœ˜—šœ ˜ K˜šžœ1žœžœž˜JKšœ^˜^Kšžœ˜—Kšœ˜Kšœ˜—šœ ˜ Kšž˜šžœ1žœžœž™JKšœj™jKšžœ™—Kšœ˜—šœ ˜ Kšœ ˜ Kšœ˜Kšžœ'žœžœ˜5šžœ1žœžœž™JKšœžœ‰™“Kšœžœq™|Kšžœ™—šžœ1žœžœž˜JKšœžœp˜zKšœžœŠ˜•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˜�—š¡œžœžœ˜@•StartOfExpansionÞ -- [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™�š¡œžœžœ"žœ˜aKš ™Kšœ žœžœžœ˜1Kšžœžœžœ$˜Ašœ˜Kšœ œ˜/Kšœ ˜ Kšœ˜Kšœ˜Kšœ˜—K˜K˜�—š¡ œžœžœ žœžœžœžœ ˜XKšœ%˜%Kš œžœžœžœžœ˜4Kšœžœ˜šžœ1žœžœž˜JKšžœ4žœ žœ™JKšœB˜BKšœ[˜[Kšžœ˜—K™7KšœH˜HKšœžœ˜+Kšœžœ˜&Kšžœžœj˜K˜K˜�—š¡ œžœžœ˜.K˜šžœ1žœžœž˜JKšœ:˜:Kšžœ˜—Kšœ˜K˜K˜�—š¡œžœ.˜;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˜�—š¡œžœžœ žœ>˜nKšœ%˜%Kšœe˜eKšœ˜Kšœ ˜ Kšœ˜K˜K˜�—š¡ œžœ%žœžœ˜GKšžœžœžœžœ˜šžœžœžœž˜3Kšœ˜Kšœ4˜4Kšœ˜Kšžœ˜—K˜K˜�—š¡œžœžœ žœ1˜]Kšžœžœžœžœ˜Kšœ ˜ Kšœ˜Kšœ˜K˜K˜�—š ¡ œžœžœ žœžœ ˜Zš¡ œžœžœ$˜>Kšœ ˜ Kšœ˜K˜—Kšœ!žœ (˜PK˜K˜�—š ¡œžœžœ žœžœ ˜Yš¡ œžœžœ$˜>Kšœ%˜%Kšœ-˜-K˜—Kšœ!žœ (˜PK˜K˜�—š ¡œžœžœ žœžœ,˜hš¡ œžœžœ$˜>Kšœ%˜%Kšœ<˜<K˜—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šœžœ˜Kš œžœžœžœžœ˜-Kšœžœžœžœ˜@Kšœžœžœžœ˜AKšœžœ˜K˜ Kšœ ˜ Kšœ˜šžœ1žœžœž˜JKšœžœžœ-žœ˜zKšžœžœX˜|KšžœžœX˜tK˜Kšžœ˜—K˜K˜�—š ¡œžœžœžœžœ˜WKšœžœ˜+Kšœ˜Kšœžœ˜Kš œžœžœžœžœ˜-K˜ Kšœ ˜ Kšœ˜šžœ1žœžœž˜JKšœžœžœ'žœ˜tšžœžœ žœ˜ KšœF˜FKšœžœžœNžœ˜sKšžœ˜K˜—K˜Kšžœ˜—K˜K˜�—š¡ œžœžœ˜HKšœžœ˜+Kšœ˜Kšœžœ˜Kš œžœžœžœžœ˜-K˜ Kšœ ˜ Kšœ˜šžœ1žœžœž˜JKšœžœžœ'žœ˜tšžœžœ˜Kšžœ˜#K˜—K˜Kšžœ˜—K˜—K˜�—™š¡œžœ˜7Kšœžœ˜K˜K˜�—š¡œžœ˜4Kšœžœ˜-K˜K˜�—š¡œžœžœ˜Ešžœžœžœžœžœžœžœž˜Jšžœžœžœ˜$Kšœžœ˜Kšž˜K˜—Kšžœ˜—K˜K˜�—š ¡œžœžœ žœžœ˜UKšœ ž˜šžœžœžœžœžœžœžœž˜JKš žœžœžœ žœžœ!˜[Kšžœ˜—K˜K˜�——™š¡œžœ$˜<Kšœžœ˜K˜K˜�—š¡œžœ$˜9Kšœžœ˜-K˜K˜�—š¡œžœžœ˜Jšžœžœžœžœžœžœžœž˜Jšžœžœžœ˜)Kšœžœ˜Kšž˜K˜—Kšžœ˜—K˜K˜�—š ¡œžœžœ žœžœ˜ZKšœžœ˜šžœžœžœžœžœžœžœž˜JKš žœžœžœ žœžœ&˜eKšžœ˜—K˜K˜�—š¡œžœžœžœžœžœ˜QKšœžœ˜Kšžœžœ!˜+K˜K˜�—š¡œžœžœžœžœžœ˜QKšœžœ˜Kšžœžœ!˜+K˜——™š¡ œžœ!˜8Kšœžœ˜K˜K˜�—š¡œžœ!˜5Kšœžœ˜,K˜K˜�—š¡œžœžœ˜Fšžœžœžœžœžœžœžœž˜Jšžœžœžœ˜'Kšœžœ˜Kšž˜K˜—Kšžœ˜—K˜K˜�—š ¡œžœžœžœžœ˜VKšœžœ˜šžœžœžœžœžœžœžœž˜JKš žœžœžœžœžœ#˜_Kšžœ˜—K˜K˜�—š¡œžœ(žœ˜EKšœ˜Kšœ˜K˜K˜�—š¡ œžœ%žœ˜EKšœ$˜$K˜——™K™�KšœžœH˜UKšœžœG˜TKšœžœH˜TKšœžœG˜RKšœžœG˜TKšœžœG˜SKšœžœG˜SKšœžœ˜KšœF˜FKšœc˜cKšœžœ ˜Kšœ žœ˜!Kšœžœ˜Kšœžœ˜Kšœžœ6˜FKšœ žœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜šœ/žœ˜4šœ!˜!Kšœ 8˜LKšœ ;˜LKšœ ˜1Kšœ "˜8Kšœ 3˜IKšœ 5˜KKšœ %˜9Kšœ2™2Kšœ:˜:Kšœ2˜2Kšœ !˜:K˜—K˜—Kšœžœžœžœžœžœ˜(Kšœžœžœžœžœžœ˜*šœ0˜0Kšœ˜K˜Kšœžœ˜K˜"K˜—šœ1˜1Kšœ˜K˜Kšœ˜Kšœ!˜!K˜—šœ0˜0Kšœ˜K˜Kšœ˜Kšœ!˜!K˜—šœ0 (˜XKšœ ˜ Kšœ˜Kšœžœ˜Kšœ$˜$K˜—šœ/˜/Kšœ ˜ Kšœ˜Kšœžœ˜Kšœ*˜*K˜—šœ1˜1Kšœ˜Kšœ ˜ Kšœ˜Kšœ*˜*K˜—šœ1˜1Kšœ˜Kšœ ˜ Kšœ˜Kšœ*˜*K˜—šœ0˜0Kšœ˜K˜ Kšœžœ˜K˜K˜—šœ2˜2Kšœ˜K˜Kšœžœ˜Kšœ˜K˜—šœ0˜0Kšœ˜K˜ Kšœžœ˜Kšœ˜K˜—Kšœ žœžœžœ-˜GKšœ žœžœžœ.˜KKšœžœžœ žœ ˜DKšœžœžœžœžœžœ˜!Kšœžœ˜Kšœžœ˜"Kšœ? ˜W—K˜�Kšžœ˜—�…—����§L��ðÇ��