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��ðÇ��