DIRECTORY Atom USING [GetPropFromList], Basics USING [BYTE, LowHalf], BasicTime USING [GMT, nullGMT], Commander USING [CommandProc, Register], CommandTool USING [ParseToList], Convert USING [CardFromRope, Error, RopeFromTime], FileNames USING [CurrentWorkingDirectory], FS USING [EnumerateForNames, NameProc, Error, StreamOpen], Graph USING [CaretIndex, CaretSpec, ColorIndex, Entity, EntityGroup, EntityGroupRec, EntityList, EntityRec, FontIndex, GRAPH, LastEntityColor, NestedEntities, NestedEntitiesList, NestedEntitiesRec, NumberOfColors, NumberOfFonts, ROPE, SegmentDataList, TargetSpec, Text, TextRec, Texts, ValueList, Viewer, XY], GraphCleanUp USING [CleanUpVL], GraphFileKeys, GraphOps USING [AddCurve, CreateGraph, SetXValues]; GraphPrivate USING [EntityGroupList, EntityHashSize, GraphHandle, GraphProc, Lock, MakeTable, PaintAll, PaintTails, ShowChart, Unlock], GraphUtil USING [FullName, HandleFromViewer, HandleNotNil, InitSegAll, InitSegEnd, LengthOfSDL, LengthOfVL, ReverseEntityList, ReverseTexts, ReverseValueList, UseMyColors, UseMyFonts, VanillaHandle], IO USING [Close, EndOf, Error, Flush, GetChar, GetIndex, int, PutBlock, PutChar, PutFR, rope, STREAM], List USING [DReverse, Kill], RefText USING [New, ObtainScratch, ReleaseScratch], Rope USING [Equal, Fetch, Find, FromRefText, IsEmpty, Length, Substr, ToRefText], ViewerClasses USING [Viewer], ViewerOps USING [DestroyViewer, PaintViewer]; GraphFile: CEDAR PROGRAM IMPORTS Atom, Basics, Commander, CommandTool, Convert, FileNames, FS, IO, List, GraphCleanUp, GraphOps, GraphPrivate, GraphUtil, RefText, Rope, ViewerOps EXPORTS GraphPrivate = { OPEN Graph, GraphFileKeys, GraphPrivate, GraphUtil; ReadBeyondEOF: SIGNAL[message: ROPE _ NIL]; SyntaxError: SIGNAL[message: ROPE _ NIL]; DataError: SIGNAL[message: ROPE _ NIL]; STREAM: TYPE = IO.STREAM; BYTE: TYPE = Basics.BYTE; CharPair: TYPE = MACHINE DEPENDENT RECORD[high, low: CHAR]; FourChars: TYPE = MACHINE DEPENDENT RECORD[lh, ll, hh, hl: CHAR]; FileFormat: TYPE = {old, binary, text, illegal}; -- currently only binary is supported. RopeList: TYPE = LIST OF ROPE; myScratch: REF TEXT _ RefText.New[256]; GraphFiles: Commander.CommandProc = { argList: RopeList; length: NAT; msg _ NIL; [argList, length] _ CommandTool.ParseToList[cmd]; IF length <= 0 THEN msg _ "To review graph files, type: Graph " ELSE { wDir: ROPE _ FileNames.CurrentWorkingDirectory[]; nGraphs: NAT _ 0; maxGraphs: NAT _ 12; allVersions: BOOL _ FALSE; FOR arg: RopeList _ argList, arg.rest UNTIL arg = NIL OR msg # NIL DO IF arg.first.Fetch[0] = '- THEN { IF arg.first.Length[] >= 2 THEN SELECT arg.first.Fetch[1] FROM 'A, 'a => allVersions _ TRUE; 'H, 'h => allVersions _ FALSE; IN ['0..'9] => { max: NAT _ Convert.CardFromRope[arg.first.Substr[1] ! Convert.Error => max _ maxGraphs]; -- don't change it maxGraphs _ max; }; ENDCASE; -- simply ignors illegal switches } ELSE { fileList: RopeList _ FileListFrom[arg.first, wDir, allVersions]; IF fileList = NIL THEN msg _ "No such file." ELSE FOR file: RopeList _ fileList, file.rest UNTIL file = NIL OR msg # NIL DO IF nGraphs >= maxGraphs THEN msg _ IO.PutFR[ "Note: max. %g plots for each command. You may change it by a switch.", IO.int[maxGraphs]] ELSE [, msg] _ HandleFromFile[file: file.first]; nGraphs _ nGraphs + 1; ENDLOOP; }; ENDLOOP; }; IF msg = NIL THEN msg _ "done."; }; -- GraphFiles FileListFrom: PROC [pattern, wDir: ROPE _ NIL, allVersions: BOOL _ FALSE] RETURNS [fileList: RopeList _ NIL] = { root, lastRoot: ROPE _ NIL; LinkIt: FS.NameProc -- PROC [fullFName] RETURNS [continue: BOOL] -- = { excl: INT _ fullFName.Find["!"]; continue _ TRUE; IF fullFName.Substr[excl-6, 6].Equal[".press", FALSE] THEN RETURN; IF ~allVersions THEN { root _ fullFName.Substr[0, excl]; IF root.Equal[lastRoot, FALSE] THEN { fileList.first _ fullFName; RETURN; }; lastRoot _ root; }; fileList _ CONS[fullFName, fileList]; }; -- LinkIt FS.EnumerateForNames[pattern, LinkIt, wDir]; TRUSTED {fileList _ LOOPHOLE[List.DReverse[LOOPHOLE[fileList]]]}; }; -- FileListFrom HandleFromFile: PUBLIC PROC[file: ROPE _ NIL] RETURNS [handle: GraphHandle _ NIL, msg: ROPE _ NIL] = { handle _ VanillaHandle[]; Lock[handle]; handle.userEditAllowed _ FALSE; msg _ GetGraph[handle, file, FALSE]; handle.userEditAllowed _ TRUE; Unlock[handle]; }; -- HandleFromFile GetGraph: PUBLIC PROC [handle: GraphHandle _ NIL, file: ROPE _ NIL] RETURNS [msg: ROPE _ NIL] = { IF HandleNotNil[handle] THEN { s: STREAM _ NIL; ok: BOOL _ TRUE; IF file.IsEmpty[] THEN RETURN["No file specified."]; s _ FS. StreamOpen[file ! FS.Error => {msg _ error.explanation; ok _ FALSE; CONTINUE} ]; IF ok THEN { ENABLE { SyntaxError => { msg _ IO.PutFR["Syntax error: %g at [%g].", IO.rope[message], IO.int[s.GetIndex[]] ]; }; ReadBeyondEOF => { msg _ IO.PutFR["Attempting to read beyond end of file for %g at [%g].", IO.rope[message], IO.int[s.GetIndex[]] ]; }; DataError => { msg _ IO.PutFR["Error: %g at [%g].", IO.rope[message], IO.int[s.GetIndex[]] ]; }; IO.EndOfStream => { msg _ End of file reached; }; IO.Error => { msg _ IO.PutFR["%g at [%g].", IO.rope[SELECT ec FROM SyntaxError => "Syntax error", Overflow => "Overflow in input conversion" ENDCASE => IO.PutFR["IO Error # %g", IO.int[LOOPHOLE[ec, CARDINAL]]]], IO.int[s.GetIndex[]] ]; }; ABORTED => { msg _ "Graph aborted. ... "; }; CONTINUE; }; IF handle.controller # NIL THEN IF handle.controller.table # NIL THEN ViewerOps.DestroyViewer[handle.controller.table]; handle.userEditAllowed _ FALSE; msg _ SELECT CheckFormat[s] FROM old => ReadPlotFile[handle, s], binary => ReadGraphFile[handle, s], text => ProcessesTextFile[handle, s], ENDCASE => "Illegal graph file."; handle.userEditAllowed _ TRUE; IF handle.controller # NIL THEN MakeTable[handle]; }; IF s # NIL THEN s.Close[]; }; }; -- GetGraph ReadPlotFile: PROC[handle: GraphHandle, s: STREAM] RETURNS [msg: ROPE _ NIL] = { OPEN handle; nNamesMax: INT; xEntity: Entity _ NIL; entityGroup: EntityGroup _ NIL; entityList: EntityList _ NIL; v1, v2: ValueList _ NIL; x1, x2: REAL _ 0.0; title: Text _ NEW[TextRec _ [ text: GetRope[s], place: [0.5, 1.1], colorIndex: 15, fontIndex: 2, justifX: center, justifY: bottom, id: NewTextId[handle, 0, FALSE]]]; time: Text _ NEW[TextRec _ [ text: Convert.RopeFromTime[GetTime[s]], place: [1.0, -0.35], colorIndex: 15, fontIndex: 1, justifX: right, justifY: bottom, id: NewTextId[handle, 1, FALSE]]]; file: Text _ NEW[TextRec _ [ text: (graph.fileName _ NARROW[Atom.GetPropFromList[s.propList, $Name]]), place: [0, -0.35], colorIndex: 15, fontIndex: 1, justifX: left, justifY: bottom, id: NewTextId[handle, 2, FALSE]]]; AppendTexts[graph.texts, CONS[title, CONS[time, CONS[file, NIL]]]]; AppendTexts[allTexts, CONS[title, CONS[time, CONS[file, NIL]]]]; graph.bounds _ [GetReal[s], GetReal[s], GetReal[s], GetReal[s]]; nNamesMax _ GetCardinal[s]; xEntity _ NEW[EntityRec _ [name: "X", id: NewEntityId[handle, 0, FALSE]]]; entityHash[xEntity.id MOD EntityHashSize] _ CONS[xEntity, entityHash[xEntity.id MOD EntityHashSize]]; entityGroup _ NEW[EntityGroupRec _ [ x: xEntity, ys: NEW[NestedEntitiesRec _ []], id: NewGroupId[handle, 0, FALSE] -- should update length later. ]]; xEntity.group _ entityGroup; AppendEGL[entityGroupList, CONS[entityGroup, NIL]]; FOR i: INT IN [1..nNamesMax] DO index: HashIndex _ i MOD EntityHashSize; entity: Entity _ NEW[EntityRec _ [ name: GetRope[s], colorIndex: ((i-1) MOD LastEntityColor) + 1, group: entityGroup, id: NewEntityId[handle, i, FALSE] ]]; entityHash[index] _ CONS[entity, entityHash[index]]; entityList _ CONS[entity, entityList]; ENDLOOP; entityGroup.ys.entityList _ ReverseEntityList[entityList, FALSE]; AppendEntityList[graph.entityList, ReverseEntityList[entityList, FALSE]]; IF chart.viewer = NIL THEN { ShowChart[handle]; -- will unlock handle right before viewer is opened up. Lock[handle]; } ELSE PaintAll[handle]; entityGroup.length _ GetCardinal[s]; FOR i: INT IN [1..entityGroup.length] DO x2 _ GetReal[s]; vl: ValueList _ CONS[x2, NIL] IF xEntity.lastValue = NIL THEN xEntity.oldValues _ xEntity.lastValue _ vl ELSE {xEntity.lastValue.rest _ vl; xEntity.lastValue _ vl}: v2 _ NIL; FOR el: EntityList _ ReverseEntityList[entityList, FALSE], el.rest UNTIL el = NIL DO y2: REAL _ GetReal[s]; yEntity: Entity _ el.first; v2 _ CONS[y2, v2]; vl _ CONS[y2, NIL]; IF yEntity.lastValue = NIL THEN yEntity.oldValues _ yEntity.lastValue _ vl ELSE {yEntity.lastValue.rest _ vl; yEntity.lastValue _ vl}: ENDLOOP; IF i > 1 THEN PaintTails[handle, paint, v1, v2, x1, x2]; -- v1 and v2 are in reversed order. v1 _ GraphCleanUp.CleanUpVL[v1]; v1 _ v2; x1 _ x2; ENDLOOP; v1 _ NIL; v2 _ GraphCleanUp.CleanUpVL[v2]; InitSegEnd[xEntity]; FOR el: EntityList _ entityList, el.rest UNTIL el = NIL DO InitSegAll[el.first]; ENDLOOP; GraphCleanUp.CleanUpEL[entityList, FALSE]; nNamesMax _ nNamesMax MOD LastEntityColor; lastEntityColor _ IF nNamesMax = 0 THEN LastEntityColor ELSE nNamesMax; }; -- ReadPlotFile ReadGraphFile: PROC [handle: GraphHandle, s: STREAM] RETURNS [msg: ROPE _ NIL] = { OPEN handle; graph.fileName _ NARROW[Atom.GetPropFromList[s.propList, $Name]]; WHILE NOT s.EndOf[] DO SELECT GetByte[s] FROM NameKey => [] _ GetRope[s, graph]; -- graph.fileName is already initialized above. TextsKey => { allTexts _ GetTexts[s]; graph.texts _ ReverseTexts[allTexts, FALSE]; graph.texts _ ReverseTexts[graph.texts, TRUE]; }; CaretsKey => GetCarets[s, graph]; ShowSlopeKey => graph.showSlope _ GetBool[s]; TargetsKey => GetTargets[s, graph]; GridsKey => GetGrids[s, graph]; DivisionsKey => GetDivisions[s, graph]; BoundsKey => GetBounds[s, graph]; ColorsKey => GetColors[s, graph]; FontsKey => GetFonts[s, graph]; EntityGroupListKey => GetEntityGroupList[s, handle]; EndOfRecordKey => EXIT; -- later we can read more than one graphs at a time. ENDCASE => SIGNAL SyntaxError["Graph"]; ENDLOOP; FOR egltemp: EntityGroupList _ entityGroupList, egltemp.rest UNTIL egltemp = NIL DO InitSegEnd[egltemp.first.x]; ENDLOOP; InitSomeCurves[handle]; IF handle.chart.viewer # NIL THEN { UseMyColors[handle]; UseMyFonts[handle]; PaintAll[handle]; } ELSE { ShowChart[handle]; -- will unlock handle. Lock[handle]; }; }; -- ReadGraphFile GraphFileProc: TYPE = PROC [s: STREAM, graph: GRAPH]; GetTexts: PROC [s: STREAM, handle: GraphHandle] RETURNS [texts: Texts _ NIL] = { DO SELECT GetByte[s] FROM EndOfListKey => EXIT; TextKey => texts _ CONS[GetText[s, handle], texts]; -- 1 ENDCASE => SIGNAL SyntaxError["Texts"]; ENDLOOP; texts _ ReverseTexts[texts, TRUE]; }; -- GetTexts GetText: PROC [s: STREAM, handle: GraphHandle] RETURNS [text: Text _ NIL] = { text _ NEW[TextRec _ []]; DO SELECT GetByte[s] FROM NameKey => text.text _ GetRope[s]; -- 1 PlaceKey => text.place _ [GetReal[s], GetReal[s]]; -- 2 FontIndexKey => text.fontIndex _ GetByte[s]; -- 3 ColorIndexKey => text.colorIndex _ GetByte[s]; -- 4 RotationKey => text.rotation _ GetReal[s]; -- 5 JustificationsKey => { -- 6 WHILE NOT s.EndOf[] DO byte: BYTE; SELECT GetByte[s] FROM XKey => { -- 1 byte _ GetByte[s]; IF byte IN [LeftKey..RightKey] THEN text.justifX _ SELECT byte FROM -- 1, 2, 3. LeftKey => left, CenterKey => center, ENDCASE => right ELSE SIGNAL SyntaxError["X Justification"]; }; YKey => { -- 2 byte _ GetByte[s]; IF byte IN [TopKey..BottomKey] THEN text.justifY _ SELECT byte FROM -- 1, 2, 3. TopKey => top, CenterKey => center, ENDCASE => bottom ELSE SIGNAL SyntaxError["Y Justification"]; }; EndOfRecordKey => EXIT; ENDCASE => SIGNAL SyntaxError["Justifications"]; ENDLOOP; }; TextIdKey => {text.id _ NewTextId[handle, GetInt[s], FALSE]}; -- 7 EndOfRecordKey => EXIT; ENDCASE => SIGNAL SyntaxError["Text"]; ENDLOOP; }; -- GetText GetCarets: GraphFileProc = { byte: BYTE; WHILE NOT s.EndOf[] DO SELECT (byte _ GetByte[s]) FROM EndOfRecordKey => EXIT; IN [PrimaryKey..TextCaretKey] => { -- 1, 2, 3. index: CaretIndex _ SELECT byte FROM PrimaryKey => primary, SecondaryKey => secondary, ENDCASE => text; GetCaret[s, graph.caret[index]]; }; ENDCASE => SIGNAL SyntaxError["Carets"]; ENDLOOP; }; -- GetCarets GetCaret: PROC [s: STREAM, spec: CaretSpec] = { WHILE NOT s.EndOf[] DO SELECT GetByte[s] FROM EndOfRecordKey => EXIT; PlaceKey => spec.place _ [GetReal[s], GetReal[s]]; -- 2 OnKey => spec.on _ GetBool[s]; -- 12 ENDCASE => SIGNAL SyntaxError["Caret"]; ENDLOOP; }; -- GetCaret GetTargets: GraphFileProc = { byte: BYTE; WHILE NOT s.EndOf[] DO SELECT (byte _ GetByte[s]) FROM EndOfRecordKey => EXIT; IN [XKey..YKey] => { -- 1, 2. xy: XY _ IF byte = XKey THEN x ELSE y; GetTarget[s, graph.target[xy]]; }; ENDCASE => SIGNAL SyntaxError["Targets"]; ENDLOOP; }; -- GetTargets GetTarget: PROC [s: STREAM, spec: TargetSpec] = { WHILE NOT s.EndOf[] DO SELECT GetByte[s] FROM EndOfRecordKey => EXIT; ValueKey => spec.value _ GetReal[s]; -- 1 WidthKey => spec.width _ GetReal[s]; -- 2 ColorIndexKey => spec.colorIndex _ GetByte[s]; -- 4, need proctection. OnKey => spec.on _ GetBool[s]; -- 12 ENDCASE => SIGNAL SyntaxError["Target"]; ENDLOOP; }; -- GetTarget GetGrids: GraphFileProc = { byte: BYTE; WHILE NOT s.EndOf[] DO SELECT (byte _ GetByte[s]) FROM EndOfRecordKey => EXIT; IN [XKey..YKey] => { -- 1, 2 xy: XY _ IF byte = XKey THEN x ELSE y; WHILE NOT s.EndOf[] DO SELECT GetByte[s] FROM EndOfRecordKey => EXIT; OnKey => graph.grids[xy] _ GetBool[s]; -- 12 ENDCASE => SIGNAL SyntaxError["Grid"]; ENDLOOP; }; ENDCASE => SIGNAL SyntaxError["Grids"]; ENDLOOP; }; -- GetGrids GetDivisions: GraphFileProc = { byte: BYTE; WHILE NOT s.EndOf[] DO SELECT (byte _ GetByte[s]) FROM EndOfRecordKey => EXIT; IN [XKey..YKey] => { -- 1, 2 xy: XY _ IF byte = XKey THEN x ELSE y; WHILE NOT s.EndOf[] DO SELECT GetByte[s] FROM EndOfRecordKey => EXIT; AutoKey => graph.auto[divisions] _ GetBool[s]; -- 9 DivisionsKey => graph.division[xy] _ GetByte[s]; -- 7 ENDCASE => SIGNAL SyntaxError["Division"]; ENDLOOP; }; ENDCASE => SIGNAL SyntaxError["Divisions"]; ENDLOOP; }; -- GetDivisions GetBounds: GraphFileProc = { byte: BYTE; WHILE NOT s.EndOf[] DO SELECT (byte _ GetByte[s]) FROM EndOfRecordKey => EXIT; IN [XKey..YKey] => { -- 1, 2 xy: XY _ IF byte = XKey THEN x ELSE y; WHILE NOT s.EndOf[] DO SELECT GetByte[s] FROM EndOfRecordKey => EXIT; AutoKey => graph.auto[bounds] _ GetBool[s]; -- 9 MaxKey => IF xy = x THEN graph.bounds.xmax _ GetReal[s] -- 1 ELSE graph.bounds.ymax _ GetReal[s]; MinKey => IF xy = x THEN graph.bounds.xmin _ GetReal[s] -- 2 ELSE graph.bounds.ymin _ GetReal[s]; ENDCASE => SIGNAL SyntaxError["Bound"]; ENDLOOP; }; ENDCASE => SIGNAL SyntaxError["Bounds"]; ENDLOOP; }; -- GetBounds GetColors: GraphFileProc = { byte: BYTE; WHILE NOT s.EndOf[] DO SELECT (byte _ GetByte[s]) FROM EndOfRecordKey => EXIT; IN [1..NumberOfColors] => { index: ColorIndex _ byte - 1; GetColor[s, graph, index]; }; ENDCASE => SIGNAL SyntaxError["Colors"]; ENDLOOP; }; -- GetColors GetColor: PROC [s: STREAM, graph: GRAPH, index: ColorIndex] = { WHILE NOT s.EndOf[] DO SELECT GetByte[s] FROM EndOfRecordKey => EXIT; RKey => graph.color[index].R _ GetReal[s]; -- 1 GKey => graph.color[index].G _ GetReal[s]; -- 2 BKey => graph.color[index].B _ GetReal[s]; -- 3 ENDCASE => SIGNAL SyntaxError["Color"]; ENDLOOP; }; -- GetColor GetFonts: GraphFileProc = { byte: BYTE; WHILE NOT s.EndOf[] DO SELECT (byte _ GetByte[s]) FROM EndOfRecordKey => EXIT; IN [1..NumberOfFonts] => { index: FontIndex _ byte - 1; GetFont[s, graph, index]; }; ENDCASE => SIGNAL SyntaxError["Fonts"]; ENDLOOP; }; -- GetFonts GetFont: PROC [s: STREAM, graph: GRAPH, index: FontIndex] = { WHILE NOT s.EndOf[] DO SELECT GetByte[s] FROM EndOfRecordKey => EXIT; NameKey => graph.font[index].family _ GetRope[s]; -- 1 BoldKey => graph.font[index].bold _ GetBool[s]; -- 2 ItalicKey => graph.font[index].italic _ GetBool[s]; -- 3 VFontSizeKey => graph.font[index].vFontSize _ GetInt[s]; -- 4 PFontScaleKey => graph.font[index].pFontScale _ GetReal[s]; -- 5 ENDCASE => SIGNAL SyntaxError["Font"]; ENDLOOP; }; -- GetFonts GetEntityGroupList: PROC [s: STREAM, handle: GraphHandle] = { OPEN handle; egl, t1, t2: EntityGroupList _ NIL; DO SELECT GetByte[s] FROM EndOfListKey => EXIT; EntityGroupKey => t1 _ CONS[GetEntityGroup[s, handle], t1]; -- 4 ENDCASE => SIGNAL SyntaxError["EntityGroupList"]; ENDLOOP; FOR t2 _ t1, t2.rest UNTIL t2 = NIL DO egl _ CONS[t2.first, egl]; ENDLOOP; IF handle.entityGroupList = NIL THEN handle.entityGroupList _ egl ELSE FOR t2 _ handle.entityGroupList, t2.rest UNTIL t2 = NIL DO IF t2.rest = NIL THEN t2.rest _ egl; ENDLOOP; List.Kill[LOOPHOLE[t1]]; }; -- GetEntityGroupList GetEntityGroup: PROC [s: STREAM, handle: GraphHandle] RETURNS [eg: EntityGroup] = { eg _ NEW[EntityGroupRec _ []]; DO SELECT GetByte[s] FROM NameKey => eg.name _ GetRope[s]; -- 1 EntityKey => eg.x _ GetEntity[s, handle, eg, NIL]; -- 2 NestedEntitiesKey => eg.ys _ GetNestedEntities[s, handle, eg, NIL]; -- 5 GroupIdKey => eg.id _ NewGroupId[GetInt[s]]; -- 6 LengthKey => eg.length _ GetInt[s]; -- 7 CrossSectionsKey => GetCrossSections[s, eg]; EndOfRecordKey => EXIT; ENDCASE => SIGNAL SyntaxError["EntityGroup"]; ENDLOOP; }; -- GetEntityGroup GetEntityList: PROC [s: STREAM, handle: GraphHandle, group: EntityGroup, father: NestedEntities] RETURNS [el: EntityList _ NIL] = { -- without getting the values, for now. DO SELECT GetByte[s] FROM EndOfListKey => EXIT; EntityKey => el _ CONS[GetEntity[s, handle, group, father], el]; -- 2 ENDCASE => SyntaxError["EntityList"]; ENDLOOP; el _ ReverseEntityList[el, TRUE]; -- resume the order. }; -- GetEntityList GetEntity: PROC [s: STREAM, handle: GraphHandle, eg: EntityGroup, father: NestedEntities _ NIL] RETURNS [entity: Entity _ NIL] = { hashIndex: [0..EntityHashSize); entity _ NEW[EntityRec _ [group: eg, parent: father]]; DO SELECT GetByte[s] FROM NameKey => entity.name _ GetRope[s]; -- 1 CommentKey => entity.comment _ GetRope[s]; -- 11 ColorIndexKey => entity.colorIndex _ GetByte[s]; -- 4 MarkKey => { -- 3 byte: BYTE _ GetByte[s]; IF byte IN [NoneKey..PercentKey] THEN -- 1 .. 7 entity.mark _ SELECT byte FROM NoneKey => none, RoundKey => round, SquareKey => square, DiamondKey => diamond, CrossKey => cross, DollarKey => dollar, ENDCASE => percent ELSE SIGNAL SyntaxError["Mark"]; }; WidthKey => entity.width _ GetReal[s]; -- 2 ValuesKey => [entity.oldValues, entity.lastValue] _ GetValueList[s]; -- 5 EntityIdKey => entity.id _ NewEntityId[handle, GetInt[s], FALSE]; -- 7 EndOfRecordKey => EXIT; ENDCASE => SIGNAL SyntaxError["Entity"]; ENDLOOP; hashIndex _ entity.id MOD EntityHashSize; handle.entityHash[hashIndex] _ CONS[entity, handle.entityHash[hashIndex]]; }; -- GetEntity GetValueList: PROC [s: STREAM] RETURNS [vl, last: ValueList _ NIL] = { length: INT _ GetInt[s]; FOR i: INT IN [0..length) DO vl _ CONS[GetReal[s], vl]; ENDLOOP; [vl, last] _ ReverseValueList[valueList, TRUE]; }; -- GetValueList GetNestedEntitiesList: PROC [s: STREAM, handle: GraphHandle, group: EntityGroup, father: NestedEntities _ NIL] RETURNS [nel: NestedEntitiesList _ NIL] = { tnel: NestedEntitiesList _ NIL; DO SELECT GetByte[s] FROM EndOfListKey => EXIT; NestedEntitiesKey => tnel _ CONS[ -- 5 GetNestedEntities[s, handle, group, father], tnel]; ENDCASE => SyntaxError["NestedEntitiesList"]; ENDLOOP; FOR ttnel: NestedEntitiesList _ tnel, ttnel.rest UNTIL ttnel = NIL DO nel _ CONS[ttnel.first, nel]; ENDLOOP; WHILE tnel # NIL DO next: NestedEntitiesList _ tnel.rest; tnel.first _ NIL; tnel.rest _ NIL; tnel _ next; ENDLOOP; }; -- GetNestedEntitiesList GetNestedEntities: PROC [s: STREAM, handle: GraphHandle, group: EntityGroup, father: NestedEntities _ NIL] RETURNS [ne: NestedEntities] = { ne _ NEW[NestedEntitiesRec _ [parent: father]]; DO SELECT GetByte[s] FROM NameKey => ne.name _ GetRope[s]; -- 1 CommentKey => ne.comment _ GetRope[s]; -- 11 EntityListKey => ne.entityList _ GetEntityList[s, handle, group, father]; -- 3 NELKey => ne.children _ GetNestedEntitiesList[s, handle, group, ne]; -- 4 EndOfRecordKey => EXIT; ENDCASE => SIGNAL SyntaxError["NestedEntities"]; ENDLOOP; }; -- GetNestedEntities GetCrossSections: PROC [s: STREAM, group: EntityGroup] = { AddValuesToEL: PROC [entityList: EntityList] = { FOR el: EntityList _ entityList, el.rest UNTIL el = NIL DO vl: ValueList _ CONS[GetReal[s], NIL]; el.first.lastValue.rest _ vl; el.first.lastValue _ vl; ENDLOOP; }; -- AddValuesToEL AddValuesToNEL: PROC [nestedEntityList: NestedEntitiesList] = { FOR nel: NestedEntitiesList _ nestedEntityList, nel.rest UNTIL nel = NIL DO AddValuesToEL[nel.first.entityList]; AddValuesToNEL[nel.first.children]; ENDLOOP; }; -- AddValuesToNEL DO -- error if eof is hit inside the loop; SELECT GetByte[s] FROM EndOfListKey => EXIT; TailsKey => { -- 8 vl: ValueList _ CONS[GetReal[s], NIL]; group.x.lastValue.rest _ vl; group.x.lastValue _ vl; AddValuesToEL[group.ys.entityList]; AddValuesToNEL[group.ys.children]; }; ENDCASE => SyntaxError["CrossSection"]; ENDLOOP; }; -- GetCrossSections InitSomeCurves: GraphProc = { OPEN handle; InitSomeEntity: PROC [ne: NestedEntities, graf: GRAPH, count: INT] RETURNS [newCount: INT] = { newCount _ count; FOR el: EntityList _ ne.entityList, el.rest UNTIL el = NIL OR newCount >= LastEntityColor DO entity: Entity _ el.first; InitSegAll[entity]; graf.entityList _ CONS[entity, graf.entityList]; newCount _ newCount + 1; ENDLOOP; FOR nel: NestedEntitiesList _ ne.children, nel.rest UNTIL nel = NIL OR newCount >= LastEntityColor DO newCount _ InitSomeEntity[nel.first, graf, newCount]; ENDLOOP; }; -- InitSomeEntity curveCount: CARDINAL _ 0; FOR egl: EntityGroupList _ entityGroupList, egl.rest UNTIL egl = NIL OR curveCount >= LastEntityColor DO curveCount _ InitSomeEntity[egl.first.ys, graph, curveCount]; ENDLOOP; graph.entityList _ ReverseEntityList[graph.entityList, TRUE]; }; -- InitSomeCurves ProcessesTextFile: PROC [handle: GraphHandle, s: STREAM] RETURNS [msg: ROPE _ NIL] = { key: ROPE _ s.GetID[ ! IO.EndOfStream => SIGNAL ReadBeyondEOF["first id in text file"]]; IF key.Equal["graph", FALSE] THEN ReadTextFile[handle, s] ELSE { s.SetIndex[0]; ReadSimpleTextFile[handle, s]; }; }; -- ReadTextFile ReadTextFile: PROC [handle: GraphHandle, s: STREAM] RETURNS [msg: ROPE _ NIL] = { msg _ "Not implemented yet." }; -- ReadGraphData ReadSimpleTextFile: PROC [handle: GraphHandle, s: STREAM] RETURNS [msg: ROPE _ NIL] = { buffer: REF TEXT _ RefText.Scratch[512]; tokenKind: IO.TokenKind _ tokenROPE; token: REF TEXT; charsSkipped: INT; error: TokenError; get, ok: BOOL _ TRUE; key: ROPE _ NIL; nNames, specifiedN: INT _ 0; nNamesSpeicified: BOOL _ FALSE; names, lastName: LIST OF ROPE _ NIL; xList: ValueList _ NIL; lvl, tlvl: LIST OF ValueList _ NIL; r: REAL; int: INT _ 0; keyRope, tokenRope: ROPE; key: {namesKey, nNamesKey, dataKey, noneKey} _ noneKey; xmin, xmax, ymin, ymax: REAL; iRow, iCol: INT _ 0; -- last row/col read so far. InitData: PROC [] = { IF nNamesSpeicified THEN { IF names = NIL THEN { FOR i: INT IN [0..nNamesSpeicified) DO names _ CONS[NIL, names]; ENDLOOP; nNames _ specifiedN; }; ELSE IF nNames # specifiedN THEN SIGNAL DataError[ IO.PutFR["%g names are specified but %g names are listed", IO.int[specifiedN], IO.int[nNames] ]]; } ELSE { IF nNames = 0 THEN SIGNAL DataError[ "Number of curves unknown before reading data"]; }; xmin _ ymin _ Real.LargestNumber; xmax _ ymax _ -xmin; FOR j: INT IN [0..nNames) DO lvl _ CONS[NIL, lvl]; ENDLOOP; tlvl _ lvl; }; -- InitData UNTIL key = dataKey DO IF get THEN { [token, ] _ s.GetToken[buffer: buffer]; tokenRope _ Rope.FromRefText[token]; }; get _ TRUE; key _ SELECT TRUE FROM tokenRope.Equal["nNames", FALSE] => nNamesKey, tokenRope.Equal["data", FALSE] => dataKey, tokenRope.Equal["names", FALSE] => namesKey, ENDCASE => SIGNAL SyntaxError["unknown key word"]; IF key = dataKey THEN InitData[]; DO [tokenKind, token, charsSkipped, error] _ s.GetCedarToken[buffer]; IF tokenKind = tokenEOF OR error # none THEN { status: ROPE; IF key = dataKey AND iCol = nNames THEN EXIT; status _ SELECT key FROM dataKey => IO.PutFR[ "after getting the %g-th number on the %g-th row in the table.", IO.int[iCol], IO.int[iRow+1]], namesKey => "getting names", ENDCASE => "getting the number of names"; IF tokenKind = tokenEOF THEN SIGNAL ReadBeyondEOF[status] ELSE SIGNAL DataError[Rope.Concat["token error in ", status]]; }; tokenRope _ Rope.FromRefText[token]; IF tokenKind = tokenCHAR THEN LOOP ELSE IF tokenKind = tokenID THEN {get _ FALSE; EXIT}; ELSE SELECT key FROM namesKey => IF tokenKind = tokenROPE THEN { nl: LIST OF ROPE _ CONS[tokenRope, NIL]; IF lastName = NIL THEN lastName _ names _ nl ELSE {lastName.rest _ nl; lastName _ nl}; nNames _ nNames + 1; IF nNamesSpeicified AND nNames > specifiedN THEN SIGNAL DataError[ IO.PutFR["%g names are specified but %g names are listed", IO.int[specifiedN], IO.int[nNames] ]; } ELSE SIGNAL SyntaxError [IO.PutFR["after getting the %g-th name", IO.int[nNames]]; }; nNamesKey => { ENABLE Convert.Error => SIGNAL DataError["number of names"]; base: [2..36] _ SELECT tokenKind FROM tokenDECIMAL => 10, tokenOCTAL => 8, tokenHEX => 16, ENDCASE => 2; number: INT _ Convert.IntFromRope[tokenRope, base]; IF names # NIL AND nNames # number THEN SIGNAL DataError[ IO.PutFR["%g names are specified but %g names are listed", IO.int[number], IO.int[nNames] ]; specifiedN _ number; nNamesSpecified _ TRUE; EXIT; }; ENDCASE => { -- dataKey ENABLE Convert.Error => SIGNAL DataError[IO.PutFR[ "parsing the %g-th number on the %g-th row", IO.int[iCol], IO.int[iRow+1]]; IF iCol = nNames THEN {iCol _ 1; iRow _ iRow + 1} ELSE iCol _ iCol + 1; r _ Convert.RealFromRope[tokenRope]; IF iCol = 1 THEN { xList _ CONS[r, xList]; IF iRow = 0 THEN xmin _ r ELSE xmax _ r; } ELSE { tlvl.first _ CONS[r, tlvl.first]; ymax _ MAX[ymax, r]; ymin _ MIN[ymin, r]; tlvl _ tlvl.rest; }; }; ENDLOOP; ENDLOOP; IF ymax = ymin THEN { IF ymax = 0.0 THEN { ymax _ 1.0; ymin _ -1.0 } ELSE { ymax _ ymax + ABS[ymax]; ymin _ ymin - ABS[ymin]; }; }; IF xmax = xmin THEN { IF xmax = 0.0 THEN { xmax _ 1.0; xmin _ -1.0 } ELSE { xmax _ xmax + ABS[xmax]; xmin _ xmin - ABS[xmin]; }; }; handle _ GraphOps.CreateGraph[ bounds: [xmin, ymin, xmax, ymax], fileName: fullName ]; [] _ GraphOps.SetXValues[handle, xList]; iCurve _ nNames; TRUSTED { lvl _ LOOPHOLE[List.DReverse[LOOPHOLE[lvl]]]; names _ LOOPHOLE[List.DReverse[LOOPHOLE[names]]]; }; tlvl _ lvl; FOR lastName _ names, lastName.rest UNTIL lastName = NIL DO [] _ GraphOps.AddCurve[handle, iCurve, tlvl.first, lastName.first, iCurve]; iCurve _ iCurve - 1; tlvl _ tlvl.rest; ENDLOOP; TRUSTED { List.Kill[LOOPHOLE[lvl]]; List.Kill[LOOPHOLE[names]]; }; out.PutF["done.\n"]; }; -- ReadSimpleGraphData SaveGraph: PUBLIC PROC[viewer: Viewer, file: ROPE _ NIL, plottedOnly: BOOL _ TRUE] RETURNS [msg: ROPE _ NIL] = { IF file.IsEmpty[] THEN msg _ "Please select a file name." ELSE IF file.Find["/", 1] > 1 OR file.Find["]"] > 1 THEN msg _ "Can not write a remote file." ELSE { handle: GraphHandle _ HandleFromViewer[viewer]; newName: ROPE; [msg, newName] _ WriteGraphFile[handle, file, plottedOnly]; IF plottedOnly THEN IF msg = NIL AND handle.chart.viewer # NIL THEN { handle.chart.viewer.name _ newName; ViewerOps.PaintViewer[handle.chart.viewer, caption, FALSE, NIL]; }; }; }; -- SaveGraph WriteGraphFile: PUBLIC PROC [handle: GraphHandle _ NIL, file: ROPE _ NIL, plottedOnly: BOOL _ TRUE] RETURNS[msg, newName: ROPE _ NIL] = { IF handle = NIL THEN RETURN["handle = NIL IN WriteGraphFile"] ELSE IF file.IsEmpty THEN RETURN ["No file name"] ELSE { OPEN handle; ok: BOOL _ TRUE; s: STREAM; nVector: CARDINAL _ 0; s _ FS. StreamOpen[fileName: file, accessOptions: $create, keep: 99 ! FS.Error => {msg _ error.explanation; ok _ FALSE; CONTINUE} ]; IF ok THEN { ENABLE IO.Error => { msg _ "Write Error."; CONTINUE }; PutRope[s, "binary graph"]; -- format id PutTexts[s, IF plottedOnly THEN graph.texts ELSE handle.allTexts]; PutCarets[s, graph]; PutByte[s, ShowSlopeKey]; PutBool[s, graph.showSlope]; PutTargets[s, graph]; PutGrids[s, graph]; PutDivisions[s, graph]; PutBounds[s, graph]; PutColors[s, graph]; PutFonts[s, graph]; PutEntityGroupList[s, handle, plottedOnly]; PutByte[s, EndOfRecordKey]; }; IF s # NIL THEN { s.Flush[]; s.Close[]; newName _ NARROW[Atom.GetPropFromList[s.propList, $Name]] }; }; }; -- WriteGraphFile PutTexts: PROC [s: STREAM, texts: Texts] = { PutByte[s, TextsKey]; FOR ts: Texts _ texts, ts.rest UNTIL ts = NIL DO text: Text _ ts.first; PutByte[s, TextKey]; PutByte[s, NameKey]; PutRope[s, text.text]; PutByte[s, PlaceKey]; PutReal[s, text.place.x]; PutReal[s, text.place.y]; PutByte[s, FontIndexKey]; PutByte[s, text.fontIndex]; PutByte[s, ColorIndexKey]; PutByte[s, text.colorIndex]; PutByte[s, RotationKey]; PutReal[s, text.rotation]; PutByte[s, JustificationsKey]; PutByte[s, XKey]; PutByte[s, SELECT text.justifX FROM left => 1, center => 2, ENDCASE => 3]; PutByte[s, YKey]; PutByte[s, SELECT text.justifY FROM top => 1, center => 2, ENDCASE => 3]; PutByte[s, EndOfRecordKey]; PutByte[s, TextIdKey]; PutInt[s, text.id]; PutByte[s, EndOfRecordKey]; ENDLOOP; PutByte[s, EndOfListKey]; }; -- PutTexts PutCarets: GraphFileProc = { PutByte[s, CaretsKey]; FOR index: CaretIndex IN CaretIndex DO PutByte[s, SELECT index FROM primary => PrimaryKey, secondary => SecondaryKey, ENDCASE => TextCaretKey]; PutByte[s, PlaceKey]; PutReal[s, graph.caret[index].place.x]; PutReal[s, graph.caret[index].place.y]; PutByte[s, OnKey]; PutBool[s, graph.caret[index].on]; PutByte[s, EndOfRecordKey]; ENDLOOP; PutByte[s, EndOfRecordKey]; }; -- PutCarets PutTargets: GraphFileProc = { PutByte[s, TargetsKey]; FOR xy: XY IN XY DO OPEN graph.target[xy]; PutByte[s, IF xy = x THEN XKey ELSE YKey]; PutByte[s, ValueKey]; PutReal[s, value]; PutByte[s, WidthKey]; PutReal[s, width]; PutByte[s, ColorIndexKey]; PutByte[s, colorIndex]; PutByte[s, OnKey]; PutBool[s, on]; PutByte[s, EndOfRecordKey]; ENDLOOP; PutByte[s, EndOfRecordKey]; }; -- PutTargets PutGrids: GraphFileProc = { PutByte[s, GridsKey]; FOR xy: XY IN XY DO PutByte[s, IF xy = x THEN XKey ELSE YKey]; PutByte[s, OnKey]; PutBool[s, graph.grids[xy]]; PutByte[s, EndOfRecordKey]; ENDLOOP; PutByte[s, EndOfRecordKey]; }; -- PutGrids PutDivisions: GraphFileProc = { PutByte[s, DivisionsKey]; FOR xy: XY IN XY DO PutByte[s, IF xy = x THEN XKey ELSE YKey]; PutByte[s, AutoKey]; PutBool[s, graph.auto[divisions]]; PutByte[s, DivisionsKey]; PutByte[s, graph.division[xy]]; PutByte[s, EndOfRecordKey]; ENDLOOP; PutByte[s, EndOfRecordKey]; }; -- PutDivisions PutBounds: GraphFileProc = { PutByte[s, BoundsKey]; FOR xy: XY IN XY DO OPEN graph.bounds; PutByte[s, IF xy = x THEN XKey ELSE YKey]; PutByte[s, AutoKey]; PutBool[s, graph.auto[bounds]]; PutByte[s, MaxKey]; PutReal[s, IF xy = x THEN xmax ELSE ymax]; PutByte[s, MinKey]; PutReal[s, IF xy = x THEN xmin ELSE ymin]; PutByte[s, EndOfRecordKey]; ENDLOOP; PutByte[s, EndOfRecordKey]; }; -- PutBounds PutColors: GraphFileProc = { PutByte[s, ColorsKey]; FOR i: ColorIndex IN ColorIndex DO OPEN graph.color[i]; PutByte[s, i+1]; PutByte[s, RKey]; PutReal[s, R]; PutByte[s, GKey]; PutReal[s, G]; PutByte[s, BKey]; PutReal[s, B]; PutByte[s, EndOfRecordKey]; ENDLOOP; PutByte[s, EndOfRecordKey]; }; -- PutColors PutFonts: GraphFileProc = { PutByte[s, FontsKey]; FOR i: FontIndex IN FontIndex DO OPEN graph.font[i]; PutByte[s, i+1]; PutByte[s, NameKey]; PutRope[s, family]; PutByte[s, BoldKey]; PutBool[s, bold]; PutByte[s, ItalicKey]; PutBool[s, italic]; PutByte[s, VFontSizeKey]; PutInt[s, vFontSize]; PutByte[s, PFontScaleKey]; PutReal[s, pFontScale]; PutByte[s, EndOfRecordKey]; ENDLOOP; PutByte[s, EndOfRecordKey]; }; -- PutFonts PutEntityGroupList: PROC [s: STREAM, handle: GraphHandle, plottedOnly: BOOL _ TRUE] = { PutByte[s, EntityGroupListKey]; FOR egl: EntityGroupList _ handle.entityGroupList, egl.rest UNTIL egl = NIL DO eg: EntityGroup _ egl.first; PutByte[s, EntityGroupKey]; PutByte[s, NameKey]; PutRope[s, eg.name]; PutEntity[s, eg.x, plottedOnly]; IF plottedOnly THEN PutPlottedNE[s, handle.graph.entityList, eg] ELSE PutAllNE[s, eg.ys]; PutByte[s, GroupIdKey]; PutInt[s, eg.id]; PutByte[s, LengthKey]; PutInt[s, eg.length]; PutByte[s, EndOfRecordKey]; ENDLOOP; PutByte[s, EndOfListKey]; }; -- PutEntityGroupList PutEntity: PROC [s: STREAM, entity: Entity, plottedOnly: BOOL _ TRUE] = { PutByte[s, EntityKey]; PutByte[s, NameKey]; PutRope[s, IF plottedOnly THEN FullName[entity] ELSE entity.name]; PutByte[s, CommentKey]; PutRope[s, IF plottedOnly THEN "" ELSE entity.comment]; PutByte[s, ColorIndexKey]; PutByte[s, entity.colorIndex]; PutByte[s, MarkKey]; PutByte[s, SELECT entity.mark FROM none => NoneKey, round => RoundKey, square => SquareKey, diamond => DiamondKey, cross => CrossKey, dollar => DollarKey, ENDCASE => PercentKey]; PutByte[s, WidthKey]; PutReal[s, entity.width]; IF plottedOnly THEN PutValuesForSDL[s, entity.segments] ELSE PutValuesForVL[s, entity.oldValues]; PutByte[s, EntityIdKey]; PutInt[s, entity.id]; PutByte[s, EndOfRecordKey]; }; -- PutEntity PutAllEL: PROC [s: STREAM, entityList: EntityList] = { PutByte[s, EntityListKey]; FOR el: EntityList _ entityList, el.rest UNTIL el = NIL DO PutEntity[s, el.first, FALSE]; ENDLOOP; PutByte[s, EndOfListKey]; }; -- PutAllEL PutPlottedEL: PROC [s: STREAM, el: EntityList, group: EntityGroup] = { PutByte[s, EntityListKey]; FOR el: EntityList _ entityList, el.rest UNTIL el = NIL DO IF el.first.group = group THEN PutEntity[s, el.first, plottedOnly]; ENDLOOP; PutByte[s, EndOfListKey]; }; -- PutPlottedEL PutPlottedNE: PROC [s: STREAM, plottedEL: EntityList, group: EntityGroup] = { PutByte[s, NestedEntitiesKey]; PutByte[s, NameKey]; PutRope[s, ne.name]; PutByte[s, CommentKey]; PutRope[s, ne.comment]; PutPlottedEL[s, plottedEL, group]; PutNEL[s, NIL]; PutByte[s, EndOfRecordKey]; }; -- PutPlottedNE PutAllNE: PROC [s: STREAM, ne: NestedEntities] = { PutByte[s, NestedEntitiesKey]; PutByte[s, NameKey]; PutRope[s, ne.name]; PutByte[s, CommentKey]; PutRope[s, ne.comment]; PutAllEL[s, ne.entityList]; PutNEL[s, ne.children]; PutByte[s, EndOfRecordKey]; }; -- PutAllNE PutNEL: PROC [s: STREAM, nestedEL: NestedEntitiesList] = { PutByte[s, NELKey]; FOR nel: NestedEntitiesList _ nestedEL, nel.rest UNTIL nel = NIL DO PutAllNE[s, nel.first]; ENDLOOP; PutByte[s, EndOfListKey]; }; -- PutNEL PutValuesForVL: PROC [s: STREAM, valueList: ValueList] = { PutByte[s, ValuesKey]; PutInt[s, LengthOfVL[valueList]]; FOR vl: ValueList _ valueList, vl.rest UNTIL vl = NIL DO PutReal[s, vl.first]; ENDLOOP; }; -- PutValuesForVL PutValuesForSDL: PROC [s: STREAM, segmentDataList: SegmentDataList] = { PutByte[s, ValuesKey]; PutInt[s, LengthOfSDL[segmentDataList]]; FOR sdl: SegmentDataList _ segmentDataList, sdl.rest UNTIL sdl = NIL DO PutReal[s, sdl.first.end]; ENDLOOP; }; -- PutValuesForSDL PutCrossSections: PROC [s: STREAM, eg: EntityGroup] = { VLL: TYPE = LIST OF ValueList; vllR, vll, tvll: VLL _ NIL; GetVLfromSDL: PROC [segmentDataList: SegmentDataList] RETURNS [vl: ValueList _ NIL] = { FOR sdl: SegmentDataList _ segmentDataList, sdl.rest UNTIL sdl = NIL DO vl _ CONS[sdl.first.end]; ENDLOOP; [vl, ] _ ReverseValueList[vl]; }; -- GetVLfromSDL GetVLsFromNE: PROC [ne: NestedEntities, old: VLL] RETURNS [new: VLL _ NIL] = { new _ old; FOR el: EntityList _ ne.entityList, el.rest UNTIL el = NIL DO new _ CONS[ IF el.first.segments = NIL THEN el.first.oldValues ELSE GetVLfromSDL[el.first.segments], new] ENDLOOP; new _ GetVLsFromNEL[ne.children, new]; }; -- GetVLsFromNE GetVLsFromNEL: PROC [nestedEntitiesList: NestedEntitiesList, old: VLL] RETURNS [new: VLL _ NIL] = { -- result is in reverse order !! new _ old; FOR nel: NestedEntitiesList _ nestedEntitiesList, nel.rest UNTIL nel = NIL DO new _ GetVLsFromNE[nel.first, new]; ENDLOOP; }; -- GetVLsFromNEL CleanUpVLL: PROC [old: VLL] RETURNS [VLL] = { WHILE old # NIL DO next: VLL _ old.rest; old.first _ NIL; old.rest _ NIL; old _ next; ENDLOOP; RETURN[NIL]; }; -- CleanUpVLL vllR _ CONS[GetVLfromSDL[eg.x.segments], NIL]; -- list of vl in reverse order. vllR _ GetVLsFromNE[eg.ys, vllR]; FOR tvll _ vllR, tvll.rest UNTIL tvll = NIL DO vll _ CONS[tvll.first, vll]; ENDLOOP; vllR _ CleanUpVLL[vllR]; PutByte[s, CrossSectionsKey]; WHILE vll # NIL DO IF vll.first = NIL THEN EXIT; PutByte[s, TailsKey]; -- keyword before each set of tails FOR tvll _ vll, tvll.rest UNTIL tvll = NIL DO vl: ValueList _ tvll.first; PutReal[s, vl.first]; tvll.first _ vl.rest; ENDLOOP; ENDLOOP; PutByte[s, EndOfListKey]; vll _ CleanUpVLL[vll]; }; -- PutCrossSections WriteDataFile: PUBLIC PROC [graph: GRAPH _ NIL, file: ROPE _ NIL] RETURNS [msg: ROPE _ NIL] = { }; -- WriteDataFile CheckFormat: PROC [stream: STREAM] RETURNS[format: FileFormat _ illegal] = { rope: ROPE; firstChar: CHAR _ stream.PeekChar[]; IF firstChar = 0C THEN { rope: ROPE _ GetRope[stream! ReadBeyondEOF => {rope _ NIL; CONTINUE}]; IF NOT rope.IsEmpty[] THEN SELECT TRUE FROM rope.Equal["* binary"] => format _ old; rope.Equal["binary graph"] => format _ binary; ENDCASE; -- illegal } ELSE format _ text; }; -- CheckFormat GetRope: PROC [stream: STREAM] RETURNS [rope: ROPE] = { length: NAT = GetCardinal[stream]; text: REF TEXT _ IF length > 256 THEN RefText.ObtainScratch[length] ELSE myScratch; FOR i: NAT IN [0..length) DO IF stream.EndOf[] THEN SIGNAL ReadBeyondEOF[ IO.PutFR["%gth character of a rope of length %g.", IO.int[i+1], IO.int[length]]]; text[i] _ stream.GetChar[]; ENDLOOP; text.length _ length; rope _ Rope.FromRefText[text]; IF length > 256 THEN RefText.ReleaseScratch[text]; }; -- GetRope GetTime: PROC [stream: STREAM] RETURNS [BasicTime.GMT] = { RETURN[LOOPHOLE[GetLongWord[stream, " a BasicTime.GMT"], BasicTime.GMT]]; }; -- GetTime GetBool: PROC [stream: STREAM] RETURNS [bool: BOOL _ FALSE] = { SELECT GetByte[stream] FROM TrueKey => bool _ TRUE; FalseKey => bool _ FALSE; ENDCASE => SIGNAL SyntaxError["Boolean"]; }; -- GetBool GetByte: PROC [stream: STREAM] RETURNS [BYTE] = { RETURN[LOOPHOLE[stream.GetChar[], BYTE]]; }; -- GetByte GetInt: PROC [stream: STREAM] RETURNS [INT] = { RETURN[LOOPHOLE[GetLongWord[stream, " an INT"], INT]]; }; -- GetInt GetReal: PROC [stream: STREAM] RETURNS [REAL] = { RETURN[LOOPHOLE[GetLongWord[stream, " a real number"], REAL]]; }; -- GetReal GetCardinal: PROC [stream: STREAM] RETURNS [CARDINAL] = { pair: CharPair; pair.high _ MyGetChar[stream, "1st byte of a cardinal"]; pair.low _ MyGetChar[stream, "2nd byte of a cardinal"]; RETURN[LOOPHOLE[pair, CARDINAL]]; }; -- GetCardinal GetLongWord: PROC [stream: STREAM, info: ROPE] RETURNS [chars: FourChars] = { chars.lh _ MyGetChar[stream, "1st byte of", info]; chars.ll _ MyGetChar[stream, "2nd byte of", info]; chars.hh _ MyGetChar[stream, "3rd byte of", info]; chars.hl _ MyGetChar[stream, "4th byte of", info]; }; -- GetLongWord MyGetChar: PROC [stream: STREAM, info1, info2: ROPE _ NIL] RETURNS [CHAR] = { IF stream.EndOf[] THEN SIGNAL ReadBeyondEOF[ IO.PutFR["%g%g.", IO.rope[info1], IO.rope[info2]]]; RETURN[stream.GetChar[]]; }; -- MyGetChar PutRope: PROC [stream: STREAM, rope: ROPE _ NIL] = { length: CARDINAL _ Basics.LowHalf[rope.Length[]]; PutCardinal[stream, length]; stream.PutBlock[Rope.ToRefText[rope], 0, length]; }; -- PutRope PutTime: PROC [stream: STREAM, time: BasicTime.GMT _ BasicTime.nullGMT] = { PutLongWord[stream, LOOPHOLE[time, FourChars]]; }; -- PutTime PutBool: PROC[stream: STREAM, bool: BOOL _ TRUE] = { PutByte[stream, IF bool THEN TrueKey ELSE FalseKey]; }; -- PutBool PutByte: PROC [stream: STREAM, byte: BYTE _ 0] = { stream.PutChar[LOOPHOLE[byte, CHAR]]; }; -- PutByte PutInt: PROC [stream: STREAM, int: INT _ 0] = { PutLongWord[stream, LOOPHOLE[int, FourChars]]; }; -- PutInt PutReal: PROC [stream: STREAM, real: REAL _ 0] = { PutLongWord[stream, LOOPHOLE[real, FourChars]]; }; -- PutReal PutCardinal: PROC [stream: STREAM, word: CARDINAL _ 0] = { pair: CharPair _ LOOPHOLE[word, CharPair]; stream.PutChar[pair.high]; stream.PutChar[pair.low]; }; -- PutCardinal PutLongWord: PROC [stream: STREAM, long: FourChars] = INLINE { stream.PutChar[long.lh]; stream.PutChar[long.ll]; stream.PutChar[long.hh]; stream.PutChar[long.hl]; }; -- PutLongCard Commander.Register["Graph", GraphFiles, "Graph , reviews the graphs in the files. See GraphDoc.tioga for more information."]; Commander.Register["Plot", GraphFiles, "Plot , reviews the graphs in the files. See GraphDoc.tioga for more information."]; }. CHANGE LOG. SChen, created at October 9, 1985 6:21:05 pm PDT. GraphFile.mesa, Copyright c 1985 by Xerox Corporation. All rights reserved. Last Edited by: Sweetsun Chen, November 7, 1985 11:30:23 pm PST scratch for rope assume handle is already locked. if no problem then return msg=nil. assume handle is already locked. texts: title, time, and file name. bounds, nNamesMax, entityHash, and entityGroupList. allocate EntityRec for each curve, construct the entity list entityGroupList.ys and graph.entityList, and create the graph viewer to show the axes, title and time. add tails v2 _ ReverseValueList[v2]; correct order of oldValues for xEntity and each entity on graph.entityList xEntity.oldValues _ ReverseValueList[xEntity.oldValues]; FOR el: EntityList _ graph.entityList, el.rest UNTIL el = NIL DO el.first.oldValues _ ReverseValueList[el.first.oldValues]; ENDLOOP; initialize each segment data lastTextId _ 1; lastEntityId _ nNamesMax; lastEntityGroupId is 0. handle will be unlocked. assume handle is already locked. The format could have been more compact. But it is easier to allow for upward/backward compatibility this way. AutoKey => graph.auto _ [GetBool[s], GetBool[s]]; initialize segments for x entities. draw some of the curves. handle shall be unlocked by caller of this proc. reads in graph.texts in the reversed order. reverse it. append it cleanup t1 reverse it. clean up. read more complicated graph file in text format. check number of names. get key word: names, nNames, or data. [] _ GraphOps.AddText[handle: handle, id: 0, rope: title, place: [0.5, 1.1], fontIndex: 2, justifX: center]; ok _ FALSE; PutCrossSections[s, eg]; result is in reverse order !! copy to vll in the correct order. clean up the reversed one. start to work. clean up. write text formatted graph file reading routines writing routines and for historical reasons Κ5€˜JšœΟmœ1™Lšœ™Icode™/—J˜šΟk ˜ Jšœžœ˜Jšœžœžœ ˜Jšœ žœžœ ˜Jšœ žœ˜(Jšœ žœ˜ Jšœžœ%˜2Jšœ žœ˜*Jšžœžœ2˜:Jš œžœlžœižœHžœ˜΅Jšœ žœ ˜J˜Jšœ žœ%˜3Jšœ žœu˜‡Jšœ žœΈ˜ΗJšžœžœVžœ˜fJšœžœ˜Jšœžœ&˜3JšœžœG˜QJšœžœ ˜Jšœ žœ˜-—J˜šœ žœž˜Jšžœ;žœžœQ˜™Jšžœ˜—J˜Jšžœ/˜3J˜Jšœžœ žœžœ˜+Jšœ žœ žœžœ˜)Jšœ žœ žœžœ˜'J˜Jšžœžœžœžœ˜Jšžœžœ žœ˜Jš œ žœžœž œžœ žœ˜;Jš œ žœžœž œžœžœ˜AJšœ žœ!Οc&˜WJš œ žœžœžœžœ˜J˜J™Jšœ žœžœ˜'J˜šΟb œ˜%Jšœ˜Jšœžœ˜ Jšœžœ˜ Jšœ1˜1Jšžœ žœ3˜Fšžœ˜Jšœžœ'˜1Jšœ žœ˜Jšœ žœ˜Jšœ žœžœ˜š žœ#žœžœžœžœž˜Ešžœžœ˜!šžœžœžœž˜>Jšœžœ˜Jšœžœ˜šžœ˜šœžœ+˜3Jšœ%Ÿ˜7—J˜J˜—JšžœŸ!˜*—J˜—šžœ˜Jšœ@˜@Jšžœ žœžœ˜,š žœžœ&žœžœžœžœž˜Nšžœžœžœ˜,JšœG˜GJšžœ˜—Jšžœ,˜0Jšœ˜Jšžœ˜—J˜—Jšžœ˜—J˜Jšžœžœžœ˜ —JšœŸ ˜J˜šΟn œžœžœžœžœžœžœžœ˜pJšœžœžœ˜š œžœ ŸΠckŸ ’Ÿ ’Ÿœ˜GJšœžœ˜ Jšœ žœ˜Jšžœ-žœžœžœ˜Bšžœžœ˜J˜!šžœžœžœ˜%Jšœ˜Jšžœ˜J˜—Jšœ˜J˜—Jšœ žœ˜%JšœŸœ˜ —J˜Jšžœ*˜,Jšžœ žœžœ˜AJšœŸ˜—J˜—š‘œžœžœžœžœžœžœžœžœ˜fJšœ˜J˜ Jšœžœ˜Jšœžœ˜$Jšœžœ˜J˜JšœŸ˜J˜—š‘œžœžœžœžœžœžœžœžœ˜aJ™ Jšœ"™"šžœžœ˜Jšœžœžœ˜Jšœžœžœ˜J˜Jšžœžœžœ˜4Jš œžœžœ)žœžœ˜XJ˜šžœžœ˜ šžœ˜˜šœžœ#˜+Jšžœ˜Jšžœ˜Jšœ˜—J˜—šœ˜šœžœ?˜GJšžœ˜Jšžœ˜Jšœ˜—J˜—˜šœžœ˜$Jšžœ˜Jšžœ˜Jšœ˜—J˜—šžœ˜Jšœ˜J˜—šžœ ˜ šœžœ˜šžœžœž˜J˜J˜*Jš žœžœžœžœžœ˜F—Jšžœ˜Jšœ˜—J˜—šžœ˜ Jšœ˜J˜—Jšžœ˜ J˜—Jš žœžœžœžœžœžœ2˜wJšœžœ˜šœžœž˜ J˜J˜#J˜%Jšžœ˜!—Jšœžœ˜Jšžœžœžœ˜2J˜—Jšžœžœžœ ˜J˜—JšœŸ ˜J˜š‘ œžœžœžœžœžœžœ˜]J™ Jšœ žœ˜Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœ˜J™J™"šœžœ ˜Jšœ˜JšœR˜RJšœžœ˜"—šœ žœ ˜Jšœ'˜'JšœS˜SJšœžœ˜"—šœ žœ ˜Jšœžœ+˜IJšœP˜PJšœžœ˜"—Jš œžœžœžœžœ˜CJš œžœžœžœžœ˜@J˜Jšœ4™4Jšœ@˜@Jšœ˜Jšœ žœ4žœ˜JJšœžœžœ žœ˜ešœžœ˜$Jšœ ˜ Jšœžœ˜ JšœžœŸ˜?Jšœ˜—J˜Jšœžœžœ˜3J™Jšœ£™£šžœžœžœž˜Jšœžœ˜(šœžœ˜"Jšœ˜Jšœžœ˜,J˜Jšœžœ˜!Jšœ˜—Jšœžœ˜4Jšœ žœ˜&Jšžœ˜—Jšœ:žœ˜AJšœAžœ˜Išžœžœžœ˜JšœŸ7˜JJ˜ J˜—Jšžœ˜J™Jšœ ™ Jšœ$˜$šžœžœžœž˜(Jšœ˜Jšœžœžœ˜Jšžœžœžœ+˜JJšžœ7˜;Jšœžœ˜ š žœ0žœ žœžœž˜TJšœžœ˜J˜Jšœžœ ˜Jšœžœžœ˜Jšžœžœžœ+˜JJšžœ7˜;Jšžœ˜—Jšœ™Jšžœžœ,Ÿ#˜\J˜ Jšœ˜Jšœ˜Jšžœ˜—Jšœžœ˜ J˜ ™JšœJ™JJšœ8™8šžœ,žœžœž™@Jšœ:™:Jšžœ™——J˜J™Jšœ˜šžœ&žœžœž˜:Jšœ˜Jšžœ˜—J™J™Jšœ™Jšœ™Jšœ#žœ˜*Jšœžœ˜*Jšœžœžœžœ ˜GJšœ™JšœŸ˜J˜—š ‘ œžœžœžœžœžœ˜RJšžœ˜ J™ J™oJšœžœ*˜Ašžœžœ ž˜šžœ ž˜Jšœ#Ÿ/˜Ršœ ˜ Jšœ˜Jšœ%žœ˜,Jšœ(žœ˜.Jšœ˜—Jšœ!˜!J˜-Jšœ#˜#Jšœ˜J˜'Jšœ!˜!Jšœ1™1Jšœ!˜!Jšœ˜Jšœ4˜4JšœžœŸ4˜LJšžœžœ˜'—Jšžœ˜—J™J™#šžœ:žœ žœž˜SJšœ˜Jšžœ˜—J˜J™Jšœ˜šžœžœžœ˜#J˜J˜Jšœ˜J˜—šžœ˜JšœŸ˜)J˜ J˜—J™0JšœŸ˜J˜Jš ‘ œžœžœžœ žœ˜5J˜š ‘œžœžœžœžœ˜PJ™+šž˜šžœ ž˜Jšœžœ˜JšœžœŸ˜8Jšžœžœ˜'—Jšžœ˜—Jšœžœ˜"JšœŸ ˜J˜—š ‘œžœžœžœžœ˜MJšœžœ˜šž˜šžœ ž˜Jšœ#Ÿ˜'Jšœ3Ÿ˜7Jšœ-Ÿ˜1Jšœ/Ÿ˜3Jšœ+Ÿ˜/šœŸ˜šžœžœ ž˜Jšœžœ˜ šžœ ž˜šœ Ÿ˜Jšœ˜šžœžœžœ˜2šžœžœŸ ˜Jšœ&žœ ˜6——Jšžœžœ ˜+J˜—šœ Ÿ˜Jšœ˜šžœžœžœ˜2šžœžœŸ ˜Jšœ$žœ ˜5——Jšžœžœ ˜+J˜—Jšœžœ˜Jšžœžœ˜0—Jšžœ˜—J˜—Jšœ5žœŸ˜BJšœžœ˜Jšžœžœ˜&——Jšžœ˜JšœŸ ˜ J˜—šœ˜Jšœžœ˜ šžœžœ ž˜šžœž˜Jšœžœ˜šžœ!Ÿ ˜.šœžœž˜$Jšœ2žœ ˜B—J˜ J˜—Jšžœžœ˜(—Jšžœ˜—JšœŸ ˜J˜š‘œžœžœ˜/šžœžœ ž˜šžœ ž˜Jšœžœ˜Jšœ3Ÿ˜7JšœŸ˜$Jšžœžœ˜'—Jšžœ˜—JšœŸ ˜—J˜—šœ˜Jšœžœ˜ šžœžœ ž˜šžœž˜Jšœžœ˜šžœŸ˜Jš œžœžœ žœžœ˜&J˜J˜—Jšžœžœ˜)—Jšžœ˜—JšœŸ ˜J˜š‘ œžœžœ˜1šžœžœ ž˜šžœ ž˜Jšœžœ˜Jšœ%Ÿ˜)Jšœ%Ÿ˜)Jšœ/Ÿ˜FJšœŸ˜$Jšžœžœ˜(—Jšžœ˜—JšœŸ ˜J˜——šœ˜Jšœžœ˜ šžœžœ ž˜šžœž˜Jšœžœ˜šžœŸ˜Jš œžœžœ žœžœ˜&šžœžœ ž˜šžœ ž˜Jšœžœ˜Jšœ'Ÿ˜,Jšžœžœ˜&—Jšžœ˜—J˜—Jšžœžœ˜'—Jšžœ˜—JšœŸ ˜J˜—šœ˜Jšœžœ˜ šžœžœ ž˜šžœž˜Jšœžœ˜šžœŸ˜Jš œžœžœ žœžœ˜&šžœžœ ž˜šžœ ž˜Jšœžœ˜Jšœ/Ÿ˜3Jšœ1Ÿ˜5Jšžœžœ˜*—Jšžœ˜—J˜—Jšžœžœ˜+—Jšžœ˜—JšœŸ˜J˜—šœ˜Jšœžœ˜ šžœžœ ž˜šžœž˜Jšœžœ˜šžœŸ˜Jš œžœžœ žœžœ˜&šžœžœ ž˜šžœ ž˜Jšœžœ˜Jšœ,Ÿ˜0šœ žœžœ Ÿ˜žœŸ˜HJšœ-Ÿ˜1Jšœ$Ÿ˜(Jšœ,˜,Jšœžœ˜Jšžœžœ˜-—Jšžœ˜—šœŸ˜J˜——š ‘ œžœžœCžœžœŸ'˜«šž˜šžœ ž˜Jšœžœ˜Jšœžœ+Ÿ˜EJšžœ˜%—Jšžœ˜—JšœžœŸ˜6JšœŸ˜J˜š ‘ œžœžœAžœžœžœ˜‚Jšœ˜Jšœ žœ*˜6šž˜šžœ ž˜Jšœ%Ÿ˜)Jšœ+Ÿ˜0Jšœ1Ÿ˜5šœ Ÿ˜Jšœžœ˜šžœžœžœŸ ˜/šœžœž˜Jšœ8˜8Jšœ>˜>Jšžœ ˜——Jšžœžœ˜ J˜—Jšœ'Ÿ˜+JšœEŸ˜IJšœ:žœŸ˜FJšœžœ˜Jšžœžœ˜(—Jšžœ˜—Jšœžœ˜)Jšœžœ'˜JJšœŸ ˜—J˜š ‘ œžœžœžœžœ˜FJšœžœ ˜šžœžœžœ ž˜Jšœžœ˜Jšžœ˜—Jšœ)žœ˜/šœŸ˜J˜———š ‘œžœžœDžœžœžœ˜šJšœžœ˜šž˜šžœ ž˜Jšœžœ˜šœžœŸ˜&Jšœ3˜3—Jšžœ&˜-—Jšžœ˜—J™ šžœ.žœ žœž˜EJšœžœ˜Jšžœ˜—J™ šžœžœž˜Jšœ%˜%Jšœ žœ˜Jšœ žœ˜J˜ Jšžœ˜—JšœŸ˜J˜—š ‘œžœžœDžœžœ˜‹Jšœžœ'˜/šž˜šžœ ž˜Jšœ!Ÿ˜%Jšœ'Ÿ˜,JšœJŸ˜NJšœEŸ˜IJšœžœ˜Jšžœžœ˜0—Jšžœ˜—JšœŸ˜J˜—š‘œžœžœ˜:š‘ œžœ˜0šžœ&žœžœž˜:Jšœžœ žœ˜&Jšœ˜J˜Jšžœ˜—JšœŸ˜—š‘œžœ+˜?šžœ6žœžœž˜KJšœ$˜$Jšœ#˜#Jšžœ˜—JšœŸ˜—šžœŸ'˜*šžœ ž˜Jšœžœ˜šœŸ˜Jšœžœ žœ˜&Jšœ˜J˜Jšœ#˜#Jšœ"˜"J˜—Jšžœ ˜'—Jšžœ˜—JšœŸ˜—J˜—šœžœ˜*š ‘œžœžœ žœžœ žœ˜^Jšœ˜šžœ(˜+Jšžœžœžœž˜0J˜Jšœ˜Jšœžœ˜0Jšœ˜Jšžœ˜—šžœ0˜3Jšžœžœžœž˜1Jšœ5˜5Jšžœ˜—JšœŸ˜J˜—Jšœ žœ˜šžœ1˜4šžœžœžœž˜3Jšœ=˜=—Jšžœ˜—Jšœ7žœ˜=JšœŸ˜J˜——š ‘œžœžœžœžœžœ˜VJšœžœžœžœ)˜XJšžœžœžœ˜9šžœ˜J˜Jšœ˜J˜—JšœŸ˜J˜—š ‘ œžœžœžœžœžœ˜QJ™0J˜JšœŸ˜J˜—š ‘œžœžœžœžœžœ˜WJšœžœžœ˜(Jšœ žœ˜$Jšœžœžœ˜Jšœžœ˜Jšœ˜J˜Jšœ žœžœ˜Jšœžœžœ˜Jšœžœ˜Jšœžœžœ˜Jš œžœžœžœžœ˜$Jšœžœ˜Jšœ žœžœ žœ˜#Jšœžœ˜Jšœžœ˜ Jšœžœ˜Jšœ7˜7Jšœžœ˜Jšœ žœŸ˜1J˜š‘œžœ˜J™šžœžœ˜šžœ žœžœ˜šžœžœžœž˜&Jšœžœžœ ˜Jšžœ˜—J˜J˜—šžœžœžœžœ ˜2šžœ8˜:Jšžœ˜Jšžœ ˜Jšœ˜——J˜—šžœ˜šžœ žœžœ ˜$Jšœ0˜0—J˜—J˜Jšœ!˜!Jšœ˜J˜Jšžœžœžœ žœžœžœžœ˜;J˜ JšœŸ ˜—J˜šžœž˜J™%šžœžœ˜ Jšœ'˜'J˜$J˜—Jšœžœ˜ šœžœžœž˜Jšœžœ˜.Jšœžœ ˜*Jšœžœ˜,Jšžœžœ!˜2—J™Jšžœžœ ˜!šž˜JšœB˜Bšžœžœžœ˜.Jšœžœ˜ Jšžœžœžœžœ˜-šœ žœž˜šœ žœ˜Jšœ@˜@Jšžœ žœ˜—Jšœ˜Jšžœ"˜)—Jšžœžœžœ˜9Jšžœžœ3˜>J˜—J˜$Jšžœžœž˜"Jš žœžœžœžœžœ˜5šžœžœž˜šœ ˜ šžœžœ˜Jš œžœžœžœžœ žœ˜(Jšžœ žœžœ˜,Jšžœ%˜)Jšœ˜šžœžœžœžœ ˜Bšžœ8˜:Jšžœ˜Jšžœ ˜Jšœ˜——Jšœ˜—Jšžœžœžœ'žœ˜RJ˜—˜Jšžœžœ˜<šœžœ ž˜%Jšœ˜Jšœ˜Jšœ˜Jšžœ˜ —Jšœžœ(˜3š žœ žœžœžœžœ ˜9šžœ8˜:Jšžœ ˜Jšžœ ˜J˜——Jšœ˜Jšœžœ˜Jšžœ˜J˜—šžœŸ ˜šžœžœ žœ˜2J˜,Jšžœ žœ˜—Jšžœžœ˜1Jšžœ˜J˜$šžœ žœ˜Jšœžœ ˜Jšžœ žœ ˜Jšžœ ˜J˜—šžœ˜Jšœ žœ˜!Jšœžœ ˜Jšœžœ ˜J˜J˜—J˜——Jšžœ˜—šžœ˜J˜——šžœ žœ˜Jšžœ žœ˜.Jšžœžœžœ ˜;J˜—šžœ žœ˜Jšžœ žœ˜.Jšžœžœžœ ˜;J˜J˜—˜J˜!Jšœ˜J˜—J˜™%J™F—Jšœ(˜(Jšœ˜šžœ˜ Jšœžœžœ˜-Jšœžœžœ ˜1J˜—J˜ šžœ!žœ žœž˜;JšœK˜KJ˜J˜Jšžœ˜—šžœ˜ Jšœ žœ˜Jšœ žœ ˜J˜—J˜—˜JšœŸ˜—J˜—š‘ œžœžœžœžœžœžœžœžœžœ˜pJšžœžœ#˜9Jšžœžœžœžœ%˜]šžœ˜J˜/Jšœ žœ˜Jšœ;˜;šžœ žœžœžœžœžœžœ˜EJšœ#˜#Jšœ4žœžœ˜@J˜—J˜—JšœŸ ˜J˜š‘œžœžœžœžœžœžœžœžœžœžœ˜‰Jšžœ žœžœžœ"˜=Jšžœžœžœžœ˜1šžœžœ˜Jšœžœžœ˜Jšœžœ˜ Jšœ žœ˜šœžœ=˜CJšœžœ)žœžœ˜@—šžœžœ˜ šžœžœ ˜Jšœ˜Jšœžœ™ Jšžœ˜ —J˜JšœŸ ˜(Jšœ žœ žœ žœ˜BJ˜J˜6J˜Jšœ˜Jšœ˜Jšœ˜J˜J˜Jšœ+˜+J˜J˜—šžœžœžœ˜Jšœ ˜ Jšœ ˜ Jšœ žœ)˜9Jšœ˜—J˜—JšœŸ˜J˜š‘œžœžœ˜,Jšœ˜šžœžœžœž˜0J˜J˜J˜Jšœ+˜+JšœI˜IJšœ5˜5Jšœ7˜7Jšœ3˜3šœ˜šœ˜Jšžœžœžœ˜?—šœ˜Jšžœžœžœ˜>—Jšœ˜—Jšœ*˜*J˜Jšœ˜Jšžœ˜—Jšœ˜JšœŸ ˜J˜—˜Jšœ˜šžœžœ ž˜&šœ žœžœ˜Jšœ2žœ˜K—šœ˜Jšœ'˜'J˜'—šœ˜J˜"—Jšœ˜Jšžœ˜—Jšœ˜JšœŸ ˜J˜—˜Jšœ˜š žœžœžœžœžœžœ˜*Jšœ žœžœžœ˜*Jšœ(˜(Jšœ(˜(Jšœ2˜2Jšœ"˜"Jšœ˜Jšžœ˜—Jšœ˜JšœŸ ˜J˜—˜Jšœ˜š žœžœžœžœž˜Jšœ žœžœžœ˜*Jšœ/˜/Jšœ˜Jšžœ˜—Jšœ˜JšœŸ ˜J˜—˜Jšœ˜š žœžœžœžœž˜Jšœ žœžœžœ˜*Jšœ7˜7Jšœ9˜9Jšœ˜Jšžœ˜—Jšœ˜JšœŸ˜J˜—˜Jšœ˜š žœžœžœžœžœžœ˜&Jšœ žœžœžœ˜*Jšœ4˜4Jšœžœžœžœ˜>Jšœžœžœžœ˜>Jšœ˜Jšžœ˜—Jšœ˜JšœŸ ˜J˜—˜Jšœ˜šžœžœ žœžœ˜7Jšœ˜Jšœ ˜ Jšœ ˜ Jšœ ˜ Jšœ˜Jšžœ˜—Jšœ˜JšœŸ ˜J˜—˜Jšœ˜šžœžœ žœžœ˜4Jšœ˜Jšœ(˜(Jšœ&˜&Jšœ*˜*Jšœ/˜/Jšœ2˜2Jšœ˜Jšžœ˜—Jšœ˜JšœŸ ˜J˜—š ‘œžœžœ$žœžœ˜WJšœ˜šžœ9žœžœž˜NJ˜J˜J˜Jšœ)˜)Jšœ ˜ Jšžœ žœ-˜@Jšžœ˜Jšœ)˜)Jšœ,˜,Jšœ™J˜Jšœ˜Jšžœ˜—Jšœ˜JšœŸ˜J˜—š ‘ œžœžœžœžœ˜IJšœ˜šœ˜Jšœ žœ žœžœ˜B—šœ˜Jšœ žœ žœžœ˜7—Jšœ9˜9šœ žœ ž˜7Jšœ8˜8Jšœ>˜>Jšžœ˜—J˜/Jšžœ žœ$˜7Jšžœ%˜)J˜.Jšœ˜JšœŸ ˜J˜—š‘œžœžœ˜6Jšœ˜šžœ&žœžœž˜:Jšœžœ˜Jšžœ˜—J˜JšœŸ ˜J˜—š‘ œžœžœ)˜FJšœ˜šžœ&žœžœž˜:Jšžœžœ%˜CJšžœ˜—J˜JšœŸ˜J˜—š‘ œžœžœ0˜MJ˜Jšœ)˜)Jšœ/˜/Jšœ"˜"Jšœ žœ˜Jšœ˜JšœŸ˜J˜—š‘œžœžœ˜2J˜Jšœ)˜)Jšœ/˜/Jšœ˜Jšœ˜Jšœ˜JšœŸ ˜J˜—š‘œžœžœ#˜:Jšœ˜šžœ.žœžœž˜CJšœ˜Jšžœ˜—Jšœ˜JšœŸ ˜ J˜—š‘œžœžœ˜:Jšœ˜Jšœ!˜!šžœ$žœžœž˜8J˜Jšžœ˜—JšœŸ˜J˜—š‘œžœžœ'˜GJšœ˜Jšœ(˜(šžœ2žœžœž˜GJ˜Jšžœ˜—šœŸ˜J˜——š‘œžœžœ˜7Jšžœžœžœžœ ˜Jšœžœžœ˜J˜š‘ œžœ$žœžœ˜Wšžœ2žœžœž˜GJšœžœ˜Jšžœ˜—J˜JšœŸœ˜—J˜š ‘ œžœžœžœžœžœ˜NJšœ™J˜ šžœ)žœžœž˜=šœžœ˜ Jšžœžœžœ˜2Jšžœ!˜%Jšœ˜—Jšžœ˜—Jšœ&˜&JšœŸ˜J˜—š ‘ œžœ/žœžœžœžœŸ ˜„J˜ šžœ8žœžœž˜MJšœ#˜#Jšžœ˜—JšœŸ˜J˜—š ‘ œžœžœžœžœ˜-šžœžœž˜Jšœžœ ˜Jšœ žœ˜Jšœ žœ˜Jšœ ˜ Jšžœ˜—Jšžœžœ˜ JšœŸ ˜—J˜JšœžœžœŸ˜NJšœ!˜!J˜Jšœ!™!šžœžœžœž˜.Jšœžœ˜Jšžœ˜—J™J™Jšœ˜J˜J™Jšœ˜šžœžœž˜Jšžœ žœžœžœ˜JšœŸ#˜9šžœžœžœž˜-J˜J˜J˜Jšžœ˜—Jšžœ˜—Jšœ˜J™J™ Jšœ˜JšœŸ˜—J˜—š‘ œžœžœ žœžœžœžœžœžœžœ˜_Jšœ™JšœŸ˜—J˜—š‘ œžœ žœžœ"˜LJšœžœ˜ Jšœ žœ˜$šžœžœ˜Jšœžœ,žœžœ˜Fš žœžœžœžœžœž˜+Jšœ'˜'Jšœ.˜.JšžœŸ ˜—J˜—Jšžœ˜JšœŸ˜J˜—Jšœ™˜š ‘œžœ žœžœžœ˜7Jšœžœ˜"Jš œžœžœžœžœžœ ˜Sšžœžœžœ ž˜šžœžœžœ˜,Jšžœ1žœ žœ˜Q—Jšœ˜Jšžœ˜—Jšœ˜Jšœ˜Jšžœžœ˜2JšœŸ ˜ J˜—š ‘œžœ žœžœ žœ˜:Jšžœžœ4žœ˜IJšœŸ ˜ J˜—š ‘œžœ žœžœžœžœ˜?šžœž˜Jšœžœ˜Jšœžœ˜Jšžœžœ˜)—JšœŸ ˜ J˜—š ‘œžœ žœžœžœ˜1Jšžœžœžœ˜)JšœŸ ˜ J˜—š ‘œžœ žœžœžœ˜/Jšžœžœ!žœ˜6JšœŸ ˜ J˜—š ‘œžœ žœžœžœ˜1Jšžœžœ(žœ˜>JšœŸ ˜ J˜—š ‘ œžœ žœžœžœ˜9J˜Jšœ8˜8Jšœ7˜7Jšžœžœžœ˜!JšœŸ˜—J˜š ‘ œžœ žœžœžœ˜MJšœ2˜2Jšœ2˜2Jšœ2˜2Jšœ2˜2JšœŸ˜J˜—š‘ œžœ žœžœžœžœžœ˜Mšžœžœžœ˜,Jšžœžœžœ˜3—Jšžœ˜JšœŸ ˜J˜—Jšœ™J˜š ‘œžœ žœžœžœ˜4Jšœžœ!˜1Jšœ˜Jšœ1˜1JšœŸ ˜ J˜—š‘œžœ žœžœ˜KJšœžœ˜/JšœŸ ˜ —J˜š ‘œžœ žœžœžœ˜4Jšœžœžœ žœ ˜4JšœŸ ˜ J˜—š‘œžœ žœžœ ˜2Jšœžœžœ˜%JšœŸ ˜ J˜—š‘œžœ žœžœ ˜/Jšœžœ˜.JšœŸ ˜ J˜—š‘œžœ žœžœ ˜2Jšœžœ˜/JšœŸ ˜ J˜—š‘ œžœ žœžœ ˜:Jšœžœ˜*Jšœ˜Jšœ˜JšœŸ˜J˜—š‘ œžœ žœžœ˜>Jšœ˜Jšœ˜Jšœ˜Jšœ˜JšœŸ˜—J˜JšœŽ˜ŽJšœ™JšœŒ˜ŒJ˜—Jšœ˜J˜šžœžœ˜ Jšœ-žœ˜1——…—(Ωθ