<> <> <> 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, ColorIndex, Entity, EntityGroup, EntityGroupRec, EntityList, EntityRec, FontIndex, GRAPH, LastEntityColor, NestedEntities, NestedEntitiesList, NestedEntitiesRec, ROPE, SegmentDataList, Text, TextRec, Texts, ValueList, Viewer, XY], GraphCleanUp USING [CleanUpVL], GraphFileKeys, GraphPrivate USING [EntityGroupList, EntityHashSize, GraphHandle, GraphProc, Lock, PaintEntity, PaintTails, ShowChart, Unlock, Wait], GraphUtil USING [FullName, HandleFromViewer, HandleNotNil, InitSegAll, InitSegEnd, ReverseEntityList, ReverseTexts, ReverseValueList, UseMyColors, UseMyFonts, VanillaHandle], IO USING [Close, EndOf, Error, Flush, GetChar, int, PutBlock, PutChar, PutFR, rope, STREAM], List USING [DReverse], 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, GraphPrivate, GraphUtil, RefText, Rope, ViewerOps EXPORTS GraphPrivate = { OPEN Graph, GraphFileKeys, GraphPrivate, GraphUtil; ReadBeyondEOF: SIGNAL[message: ROPE _ NIL]; SyntaxError: SIGNAL[message: ROPE _ NIL]; 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]; 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[]; msg _ GetGraph[handle, file]; IF msg = NIL THEN ShowChart[handle]; }; -- HandleFromFile GetGraph: PUBLIC PROC [handle: GraphHandle _ NIL, file: ROPE _ NIL] RETURNS [msg: ROPE _ NIL] = { -- if no problem then return msg=nil. IF HandleNotNil[handle] THEN { s: IO.STREAM _ NIL; ok: BOOL _ TRUE; Lock[handle]; IF file.IsEmpty[] THEN RETURN["No file specified."]; s _ FS. StreamOpen[file ! FS.Error => {msg _ error.explanation; ok _ FALSE; CONTINUE} ]; IF ok THEN { ENABLE { ReadBeyondEOF => { msg _ IO.PutFR["Attempting to read beyond end of file for the %g.", IO.rope[message]]; CONTINUE; }; IO.Error => { msg _ IO.PutFR["IO Error # %g in ReadGraphFile.", IO.int[LOOPHOLE[ec, CARDINAL]]]; CONTINUE; }; ABORTED => { msg _ "Graph aborted. ... "; CONTINUE; }; }; IF handle.controller # NIL THEN IF handle.controller.table # NIL THEN ViewerOps.DestroyViewer[handle.controller.table]; msg _ SELECT CheckFormat[s] FROM old => ReadPlotFile[handle, s], binary => ReadGraphFile[handle, s], text => ReadDataFile[handle, s], ENDCASE => "Illegal graph file."; }; IF s # NIL THEN s.Close[]; Unlock[handle]; }; }; -- GetGraph ReadPlotFile: PROC[handle: GraphHandle, s: IO.STREAM] RETURNS [msg: ROPE _ NIL] = { OPEN handle; nCurvesMax: CARDINAL; 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: 1, justifX: center, justifY: bottom, id: 0]]; time: Text _ NEW[TextRec _ [ text: Convert.RopeFromTime[GetTime[s]], place: [1.0, -0.35], colorIndex: 15, justifX: right, justifY: bottom, id: 1]]; file: Text _ NEW[TextRec _ [ text: (graph.fileName _ NARROW[Atom.GetPropFromList[s.propList, $Name]]), place: [0, -0.35], colorIndex: 15, justifX: left, justifY: bottom, id: 2]]; graph.texts _ CONS[file, CONS[time, CONS[title, NIL]]]; allTexts _ CONS[file, CONS[time, CONS[title, NIL]]]; <> graph.bounds _ [GetReal[s], GetReal[s], GetReal[s], GetReal[s]]; nCurvesMax _ GetCardinal[s]; xEntity _ NEW[EntityRec _ [name: "X"]]; entityHash[0] _ CONS[xEntity, entityHash[0]]; <> entityGroup _ NEW[EntityGroupRec _ [ x: xEntity, ys: CONS[NEW[NestedEntitiesRec _ []], NIL] -- id = 0, should update length later. ]]; xEntity.group _ entityGroup; entityGroupList _ CONS[entityGroup, entityGroupList]; <<>> <> FOR i: CARDINAL IN [1..nCurvesMax] DO entity: Entity _ NEW[EntityRec _ [ name: GetRope[s], colorIndex: ((i-1) MOD LastEntityColor) + 1, group: entityGroup, id: i]]; entityHash[i] _ CONS[entity, entityHash[i]]; entityList _ CONS[entity, entityList]; ENDLOOP; graph.entityList _ ReverseEntityList[entityList, FALSE]; entityGroup.ys.first.entityList _ ReverseEntityList[entityList, TRUE]; UseMyColors[handle]; UseMyFonts[handle]; waitingGraph _ TRUE; ShowChart[handle]; -- will unlock handle. WHILE waitingGraph DO Wait[]; ENDLOOP; Lock[handle]; <<>> <> entityGroup.length _ GetCardinal[s]; FOR i: INT IN [1..entityGroup.length] DO x2 _ GetReal[s]; xEntity.oldValues _ CONS[x2, xEntity.oldValues]; <> v2 _ NIL; FOR el: EntityList _ graph.entityList, el.rest UNTIL el = NIL DO y2: REAL _ GetReal[s]; v2 _ CONS[y2, v2]; el.first.oldValues _ CONS[y2, el.first.oldValues]; ENDLOOP; v2 _ ReverseValueList[v2]; IF i > 1 THEN { < 0.0 DO Wait[handle] ENDLOOP;>> PaintTails[handle, paint, v1, v2, x1, x2]; }; v1 _ v2; x1 _ x2; ENDLOOP; v1 _ GraphCleanUp.CleanUpVL[v1]; v2 _ GraphCleanUp.CleanUpVL[v2]; <<>> <> xEntity.oldValues _ ReverseValueList[xEntity.oldValues]; FOR el: EntityList _ graph.entityList, el.rest UNTIL el = NIL DO el.first.oldValues _ ReverseValueList[el.first.oldValues]; ENDLOOP; <> InitSegEnd[xEntity]; FOR el: EntityList _ graph.entityList, el.rest UNTIL el = NIL DO InitSegAll[el.first]; ENDLOOP; <<>> <> <> <> lastEntityColor _ ((nCurvesMax - 1) MOD LastEntityColor) + 1; }; -- ReadPlotFile ReadGraphFile: PROC [handle: GraphHandle, s: IO.STREAM] RETURNS [msg: ROPE _ NIL] = { OPEN handle; egl: EntityGroupList; graph.fileName _ NARROW[Atom.GetPropFromList[s.propList, $Name]]; <> WHILE NOT s.EndOf[] DO SELECT GetByte[s] FROM <<-- NameKey => graph.fileName _ GetRope[s, graph];>> TextsKey => { texts: Texts _ GetTexts[s]; allTexts _ ReverseTexts[texts, FALSE]; graph.texts _ ReverseTexts[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]; < graph.auto _ [GetBool[s], GetBool[s]];>> ColorsKey => GetColors[s, graph]; FontsKey => GetFonts[s, graph]; EntitiesKey => GetEntityGroupList[s, handle]; EndOfRecordKey => EXIT; -- later we can read more than one graphs at a time. ENDCASE => SIGNAL SyntaxError["Illegal specification for graph fields."]; ENDLOOP; <<>> <> <> FOR egltemp: EntityGroupList _ entityGroupList, egltemp.rest UNTIL egltemp = NIL DO eg: EntityGroup _ egltemp.first; eg.x.oldValues _ ReverseValueList[eg.x.oldValues]; -- x ReorderValuesOnNEL[eg.ys]; -- ys ENDLOOP; <> FOR egltemp: EntityGroupList _ entityGroupList, egltemp.rest UNTIL egltemp = NIL DO InitSegEnd[egltemp.first.x]; ENDLOOP; UseMyColors[handle]; UseMyFonts[handle]; waitingGraph _ TRUE; ShowChart[handle]; -- will unlock handle. WHILE waitingGraph DO Wait[] ENDLOOP; Lock[handle]; PaintSomeCurves[handle]; }; -- ReadGraphFile GraphFileProc: TYPE = PROC [s: IO.STREAM, graph: GRAPH]; GetTexts: PROC [s: STREAM] RETURNS [ts: Texts _ NIL] = { <> text: Text; byte: BYTE; WHILE NOT s.EndOf[] DO SELECT (byte _ GetByte[s]) FROM EndOfListKey => EXIT; ENDCASE => { text _ NEW[TextRec _ []]; DO SELECT byte FROM NameKey => text.text _ GetRope[s]; PlaceKey => text.place _ [GetReal[s], GetReal[s]]; FontIndexKey => text.fontIndex _ GetByte[s]; ColorIndexKey => text.colorIndex _ GetByte[s]; RotationKey => text.rotation _ GetReal[s]; JustificationsKey => { WHILE NOT s.EndOf[] DO SELECT (byte _ GetByte[s]) FROM XKey => { byte _ GetByte[s]; IF byte IN [LeftKey..RightKey] THEN text.justifX _ SELECT byte FROM LeftKey => left, CenterKey => center, ENDCASE => right ELSE SIGNAL SyntaxError["justifX"]; }; YKey => { byte _ GetByte[s]; IF byte IN [LeftKey..RightKey] THEN text.justifY _ SELECT byte FROM TopKey => top, CenterKey => center, ENDCASE => bottom ELSE SIGNAL SyntaxError["justifY"]; }; EndOfRecordKey => EXIT; ENDCASE => SIGNAL SyntaxError["Justification"]; ENDLOOP; }; TextIdKey => {text.id _ GetInt[s]}; EndOfRecordKey => {texts _ CONS[text, texts]; EXIT}; ENDCASE => SIGNAL SyntaxError["Text"]; byte _ IF s.EndOf[] THEN EndOfRecordKey ELSE GetByte[s]; ENDLOOP; }; ENDLOOP; }; -- GetTexts GetCarets: GraphFileProc = { byte: BYTE; WHILE NOT s.EndOf[] DO SELECT (byte _ GetByte[s]) FROM EndOfRecordKey => EXIT; IN [PrimaryKey..TextCaretKey] => { 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]]; OnKey => spec.on _ GetBool[s]; 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] => { 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]; WidthKey => spec.width _ GetReal[s]; ColorIndexKey => GetByte[s]; -- need proctection. OnKey => spec.on _ GetBool[s]; 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] => { xy: XY _ IF byte = XKey THEN x ELSE y; WHILE NOT s.EndOf[] DO SELECT GetByte[s] FROM EndOfRecordKey => EXIT; OnKey => graph.grid[xy] _ GetBool[s]; 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] => { 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]; DivisionsKey => graph.division[xy] _ GetByte[s]; ENDCASE => SIGNAL SyntaxError["Grid"]; ENDLOOP; }; ENDCASE => SIGNAL SyntaxError["Grids"]; ENDLOOP; }; -- GetDivisions GetBounds: GraphFileProc = { byte: BYTE; WHILE NOT s.EndOf[] DO SELECT (byte _ GetByte[s]) FROM EndOfRecordKey => EXIT; IN [XKey..YKey] => { 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]; MaxKey => IF xy = x THEN graph.bounds.xmax _ GetReal[s] ELSE graph.bounds.ymax _ GetReal[s]; MinKey => IF xy = x THEN graph.bounds.xmin _ GetReal[s] 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 ColorIndex => { index: CaretIndex _ byte; 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]; GKey => graph.color[index].g _ GetReal[s]; BKey => graph.color[index].b _ GetReal[s]; ENDCASE => SIGNAL SyntaxError["Color"]; ENDLOOP; }; -- GetColor GetFonts: GraphFileProc = { byte: BYTE; WHILE NOT s.EndOf[] DO SELECT (byte _ GetByte[s]) FROM EndOfRecordKey => EXIT; IN FontIndex => { index: FontIndex _ byte; 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]; BoldKey => graph.font[index].bold _ GetBool[s]; ItalicKey => graph.font[index].italic _ GetBool[s]; VFontSizeKey => graph.font[index].vFontSize _ GetInt[s]; PFontScaleKey => graph.font[index].pFontScale _ GetReal[s]; ENDCASE => SIGNAL SyntaxError["Font"]; ENDLOOP; }; -- GetFonts GetEntityGroupList: PROC [s: IO.STREAM, handle: GraphHandle] = { OPEN handle; eg: EntityGroup; egl: EntityGroupList _ NIL; byte: BYTE; WHILE NOT s.EndOf[] DO SELECT (byte _ GetByte[s]) FROM EndOfListKey => EXIT; ENDCASE => { eg _ NEW[EntityGroupRec _ []]; DO SELECT byte FROM <<-- NameKey => eg.name _ GetRope[s];>> XEntityKey => eg.x _ GetEntity[s, handle, eg, NIL]; <<-- EntityListKey => eg.ys _ GetEntityList[s, handle, eg];>> NELKey => eg.ys _ GetNestedEntitiesList[s, handle, eg, NIL]; GroupIdKey => eg.id _ GetInt[s]; LengthKey => eg.length _ GetInt[s]; CrossSectionsKey => GetCrossSections[s, eg]; EndOfRecordKey => {egl _ CONS[eg, egl]; EXIT}; ENDCASE => SIGNAL SyntaxError["EntityGroup"]; byte _ IF s.EndOf[] THEN EndOfRecordKey ELSE GetByte[s]; ENDLOOP; }; ENDLOOP; <<>> <> FOR tegl: EntityGroupList _ egl, tegl.rest UNTIL tegl = NIL DO handle.entityGroupList _ CONS[tegl.first, handle.entityGroupList]; ENDLOOP; <<>> <> WHILE egl # NIL DO next: EntityGroupList _ egl.rest; egl.first _ NIL; egl.rest _ NIL; egl _ next; ENDLOOP; }; -- GetEntityGroupList GetEntityList: PROC [s: IO.STREAM, handle: GraphHandle, group: EntityGroup, father: NestedEntities] RETURNS [el: EntityList _ NIL] = { -- without getting the values, for now. entity: Entity; byte: BYTE; WHILE NOT s.EndOf[] DO SELECT (byte _ GetByte[s]) FROM EndOfListKey => EXIT; ENDCASE => { WHILE NOT s.EndOf[] DO entity: Entity _ GetEntity[s, handle, group, father]; IF entity = NIL THEN EXIT ELSE el _ CONS[entity, el]; ENDLOOP; ENDLOOP; el _ ReverseEntityList[el, TRUE]; -- resume the order. }; -- GetEntityList GetEntity: PROC [s: IO.STREAM, handle: GraphHandle, eg: EntityGroup, father: NestedEntities _ NIL] RETURNS [entity: Entity _ NIL] = { hashIndex: [0..EntityHashSize); byte: BYTE _ GetByte[s]; IF byte # EndOfRecordKey THEN { entity _ NEW[EntityRec _ [group: eg, parent: father]]; WHILE byte # EndOfRecordKey DO SELECT byte FROM NameKey => entity.name _ GetRope[s]; ColorIndexKey => entity.colorIndex _ GetByte[s]; MarkKey => { byte _ GetByte[s]; IF byte IN [NoneKey..PercentKey] THEN 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]; <<-- ValuesKey => entity.newValues _ GetValueList[s];>> EntityIdKey => entity.id _ GetInt[s]; <<-- LevelKey => entity.level _ GetInt[s];>> EndOfRecordKey => EXIT; ENDCASE => SIGNAL SyntaxError["Entity"]; byte _ IF s.EndOf[] THEN EndOfRecordKey ELSE GetByte[s]; ENDLOOP; hashIndex _ entity.id MOD EntityHashSize; handle.entityHash[hashIndex] _ CONS[entity, handle.entityHash[hashIndex]]; }; }; -- GetEntity GetNestedEntitiesList: PROC [s: IO.STREAM, handle: GraphHandle, group: EntityGroup, father: NestedEntities _ NIL] RETURNS [nel: NestedEntitiesList _ NIL] = { byte: BYTE; ne: NestedEntities; WHILE NOT s.EndOf[] DO SELECT (byte _ GetByte[s]) FROM EndOfListKey => EXIT; ENDCASE => { ne _ NEW[NestedEntitiesRec _ [parent: father]]; DO SELECT byte FROM NameKey => ne.name _ GetRope[s]; EntityListKey => ne.entityList _ GetEntityList[s, handle, group, father]; ChildrenKey => ne.children _ GetNestedEntitiesList[s, handle, group, ne]; EndOfRecordKey => {nel _ CONS[ne, nel]; EXIT}; ENDCASE => SIGNAL SyntaxError["NestedEntities"]; byte _ IF s.EndOf[] THEN EndOfRecordKey ELSE GetByte[s]; ENDLOOP; }; ENDLOOP; }; -- GetNestedEntities GetCrossSections: PROC [s: IO.STREAM, group: EntityGroup] = { AddValuesToEL: PROC [entityList: EntityList] = { FOR el: EntityList _ entityList, el.rest UNTIL el = NIL DO OPEN el.first; oldValues _ CONS[GetReal[s], oldValues]; 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 byte: BYTE; WHILE NOT s.EndOf[] DO -- error if eof is hit inside the loop; IF (byte _ GetByte[s]) = EndOfListKey THEN EXIT ELSE IF byte = TailsKey THEN { group.x.oldValues _ CONS[GetReal[s], group.x.oldValues]; AddValuesToNEL[group.ys]; }; ENDLOOP; }; -- GetCrossSections ReorderValuesOnNEL: PROC [nestedEntitiesList: NestedEntitiesList] = { FOR nel: NestedEntitiesList _ nestedEntitiesList, nel.rest UNTIL nel = NIL DO FOR el: EntityList _ nel.first.entityList, el.rest UNTIL el = NIL DO -- ys el.first.oldValues _ ReverseValueList[el.first.oldValues]; ENDLOOP; ReorderValuesOnNEL[nel.first.children]; ENDLOOP; }; -- ReorderValuesOnNEL PaintSomeCurves: GraphProc = { OPEN handle; PaintSomeOnEntityList: PROC [entityList: EntityList] = { FOR el: EntityList _ entityList, el.rest UNTIL el = NIL OR curveCount >= LastEntityColor DO entity: Entity _ el.first; InitSegAll[entity]; graph.entityList _ CONS[entity, graph.entityList]; PaintEntity[handle, paint, entity]; curveCount _ curveCount + 1; ENDLOOP; }; -- PaintSomeOnEntityList curveCount: CARDINAL _ 0; < 0.0 DO Wait[handle] ENDLOOP;>> FOR egl: EntityGroupList _ entityGroupList, egl.rest UNTIL egl = NIL OR curveCount >= LastEntityColor DO entityGroup: EntityGroup _ egl.first; FOR nel: NestedEntitiesList _ entityGroup.ys, nel.rest UNTIL nel = NIL OR curveCount >= LastEntityColor DO PaintSomeOnEntityList[nel.first.entityList]; ENDLOOP; ENDLOOP; }; -- PaintSomeCurves ReadDataFile: PROC [handle: GraphHandle, s: IO.STREAM] RETURNS [msg: ROPE _ NIL] = { <> }; -- ReadDataFile SaveGraph: PUBLIC PROC[viewer: Viewer, file: ROPE _ NIL] RETURNS [msg: ROPE _ NIL] = { IF toFile.IsEmpty[] THEN msg _ "Please select a file name." ELSE IF toFile.Find["/", 1] > 1 OR toFile.Find["]"] > 1 THEN msg _ "Can not write a remote file." ELSE { handle: GraphHandle _ HandleFromViewer[viewer]; newName: ROPE; [msg, newName] _ WriteGraphFile[handle.graph, file]; 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 [ref: REF ANY _ NIL, file: ROPE _ NIL] RETURNS[msg, newName: ROPE _ NIL] = { handle: GraphHandle _ NIL; graph: GRAPH _ NIL; IF ref # NIL THEN RETURN["ref = NIL IN WriteGraphFile"]; IF ISTYPE[ref, GraphHandle] THEN { handle _ NARROW[ref]; graph _ handle.graph } ELSE IF ISTYPE[ref, GRAPH] THEN graph _ NARROW[ref]; IF graph = NIL THEN RETURN ["Graph = NIL"] ELSE IF file.IsEmpty THEN RETURN ["No file name"] ELSE { excl: INT _ file.Find["!"]; fileNameRoot: ROPE = IF excl < 0 THEN file ELSE file.Substr[0, excl]; ok: BOOL _ TRUE; s: IO.STREAM; nVector: CARDINAL _ 0; fileName: ROPE _ IF file.Find["."] < 0 THEN ReplaceFileExt[ 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 handle = NIL THEN graph.texts ELSE handle.allTexts]; PutCarets[s, graph]; PutByte[s, ShowSlopeKey]; PutBool[s, graph.showSlope]; PutTargets[s, graph]; PutByte[s, GridsKey]; PutBool[s, graph.grids[x]]; PutBool[s, graph.grids[y]]; PutByte[s, DivisionsKey]; PutByte[s, graph.division[x]]; PutByte[s, graph.division[y]]; PutByte[s, BoundsKey]; {OPEN graph.bounds; PutReal[s, xmin]; PutReal[s, ymin]; PutReal[s, xmax]; PutReal[s, ymax];}; PutByte[s, AutoKey]; PutBool[s, graph.auto[divisions]]; PutBool[s, graph.auto[bounds]]; PutColors[s, graph]; PutFonts[s, graph]; PutEntityGroupList[s, handle, graph]; PutByte[s, EndOfRecordKey]; }; IF s # NIL THEN {s.Flush[]; s.Close[]}; }; }; -- WriteGraphFile PutTexts: PROC [s: IO.STREAM, texts: Texts] = { FOR ts: Texts _ texts, ts.rest UNTIL ts = NIL DO text: Text _ ts.first; 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 = { FOR index: CaretIndex IN CaretIndex DO PutByte[s, SELECT index FROM primary => PrimaryKey, secondary => SecondaryKey, ENDCASE => TextCaretKey]; PutReal[s, graph.caret[index].place.x]; PutReal[s, graph.caret[index].place.y]; PutBool[s, graph.caret[index].on]; ENDLOOP; PutByte[s, EndOfRecordKey]; }; -- PutCarets PutTargets: GraphFileProc = { FOR xy: XY IN XY DO OPEN graph.target[xy]; PutReal[s, value]; PutReal[s, width]; PutByte[s, colorIndex]; PutBool[s, on]; ENDLOOP; }; -- PutTargets PutColors: GraphFileProc = { PutByte[s, LAST[ColorIndex]+1]; FOR i: ColorIndex IN ColorIndex DO OPEN graph.color[i]; PutReal[s, R]; PutReal[s, G]; PutReal[s, B]; ENDLOOP; }; -- PutColors PutFonts: GraphFileProc = { PutByte[s, LAST[FontIndex]+1]; FOR i: FontIndex IN FontIndex DO OPEN graph.font[i]; PutRope[s, family]; PutBool[s, bold]; PutBool[s, italic]; PutByte[s, vFontSize]; PutReal[s, pFontScale]; ENDLOOP; }; -- PutFonts PutEntityGroupList: PROC [s: IO.STREAM, handle: GraphHandle, graph: GRAPH] = { simple: BOOL _ handle = NIL; entityGroupList: EntityGroupList _ NIL; IF simple THEN FOR el: EntityList _ graph.entityList, el.rest UNTIL el = NIL DO entityGroupList _ Enlist[el.first, entityGroupList]; ENDLOOP ELSE entityGroupList _ handle.entityGroupList; FOR egl: EntityGroupList _ entityGroupList, egl.rest UNTIL egl = NIL DO eg: EntityGroup _ egl.first; <<-- PutByte[s, NameKey]; PutRope[s, eg.name];>> PutByte[s, XEntityKey]; PutEntity[s, eg.x, simple]; <<-- PutByte[s, EntityListKey]; PutEntityList[s, eg.ys, simple];>> PutByte[s, NELKey]; PutNestedEntitiesList[s, eg.ys, simple]; PutByte[s, GroupIdKey]; PutInt[s, eg.id]; PutByte[s, LengthKey]; PutInt[s, eg.length]; PutByte[s, CrossSectionsKey]; PutCrossSections[s, eg]; PutByte[s, EndOfRecordKey]; ENDLOOP; PutByte[s, EndOfListKey]; }; -- PutEntityGroupList Enlist: PROC [entity: Entity _ NIL, oldEGL: EntityGroupList _ NIL] RETURNS [newEGL: EntityGroupList] = { -- enlist on top level only. newEGL _ oldEGL; IF entity # NIL THEN { groupId: INT _ entity.group.id; groupOnList: BOOL _ FALSE; FOR egl: EntityGroupList _ newEGL, egl.rest UNTIL egl = NIL DO group: EntityGroup _ egl.first; IF groupId = group.id THEN { groupOnList _ TRUE; group.ys.first.entityList _ CONS[entity, group.ys.first.entityList]; EXIT; }; ENDLOOP; IF NOT groupOnList THEN { entityGroup: EntityGroup _ NEW[EntityGroupRec _ [ x: entity.group.x, ys: CONS[NEW[NestedEntitiesRec _ [entityList: CONS[entity, NIL]]], NIL], id: groupId, length: entity.group.length ]]; newEGL _ CONS[entityGroup, newEGL]; }; }; }; -- Enlist PutEntity: PROC [s: IO.STREAM, entity: Entity, simple: BOOL _ FALSE] = { PutByte[s, NameKey]; PutRope[s, IF simple THEN FullName[entity] ELSE entity.name]; 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]; <> PutByte[s, EntityIdKey]; PutInt[s, entity.id]; <<-- PutByte[s, LevelKey]; PutInt[s, entity.level];>> PutByte[s, EndOfRecordKey]; }; -- PutEntity PutEntityList: PROC [s: IO.STREAM, entityList: EntityList, simple: BOOL _ FALSE] = { FOR el: EntityList _ entityList, el.rest UNTIL el = NIL DO PutEntity[s, el.first, simple]; ENDLOOP; PutByte[s, EndOfListKey]; }; -- PutEntityList PutNestedEntitiesList: PROC [s: IO.STREAM, nestedEL: NestedEntitiesList, simple: BOOL _ FALSE] = { FOR nel: NestedEntitiesList _ nestedEL, nel.rest UNTIL nel = NIL DO ne: NestedEntities _ nel.first; PutByte[s, NameKey]; PutRope[s, ne.name]; PutByte[s, EntityListKey]; PutEntityList[s, ne.entityList, simple]; PutByte[s, ChildrenKey]; PutNestedEntitiesList[s, ne.children, simple]; PutByte[s, EndOfRecordKey]; ENDLOOP; PutByte[s, EndOfListKey]; }; -- PutNestedEntitiesList PutCrossSections: PROC [s: IO.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 GetVLsFromNEL: PROC [nestedEntitiesList: NestedEntitiesList, old: VLL] RETURNS [new: VLL _ NIL] = { -- result is in reverse order !! FOR nel: NestedEntitiesList _ nestedEntitiesList, nel.rest UNTIL nel = NIL DO FOR el: EntityList _ nel.first.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[nel.first.children, 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], vllR]; vllR _ GetVLsFromNEL[eg.ys, vllR]; -- in reverse order <> FOR tvll _ vllR, tvll.rest UNTIL tvll = NIL DO vll _ CONS[tvll.first, vll]; ENDLOOP; <<>> <> vllR _ CleanUpVLL[vllR]; <> 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 <> GetRope: PROC [stream: IO.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: IO.STREAM] RETURNS [BasicTime.GMT] = { RETURN[LOOPHOLE[GetLongWord[stream, " a BasicTime.GMT"], BasicTime.GMT]]; }; -- GetTime GetBool: PROC [stream: IO.STREAM] RETURNS [bool: BOOL _ FALSE] = { SELECT stream.GetByte[] FROM TrueKey => bool _ TRUE; FalseKey => bool _ FALSE; ENDCASE => SIGNAL SyntaxError["GetBool"]; }; -- GetBool GetByte: PROC [stream: IO.STREAM] RETURNS [BYTE] = { RETURN[LOOPHOLE[stream.GetChar[], BYTE]]; }; -- GetByte GetInt: PROC [stream: IO.STREAM] RETURNS [INT] = { RETURN[LOOPHOLE[GetLongWord[stream, " an INT"], INT]]; }; -- GetInt GetReal: PROC [stream: IO.STREAM] RETURNS [REAL] = { RETURN[LOOPHOLE[GetLongWord[stream, " a real number"], REAL]]; }; -- GetReal GetCardinal: PROC [stream: IO.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: IO.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: IO.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 CheckFormat: PROC [stream: IO.STREAM] RETURNS[format: FileFormat _ illegal] = { rope: ROPE; rope _ GetRope[stream ! ReadBeyondEOF => {rope _ NIL; CONTINUE}]; IF NOT rope.IsEmpty[] THEN RETURN[SELECT TRUE FROM rope.Equal["* binary"] => old, rope.Equal["binary graph"] => binary, rope.Equal["Graph", FALSE] => text, -- text format will be legal later ENDCASE => illegal]; }; -- CheckFormat <> PutRope: PROC [stream: IO.STREAM, rope: ROPE _ NIL] = { length: CARDINAL _ Basics.LowHalf[rope.Length[]]; PutCardinal[stream, length]; stream.PutBlock[Rope.ToRefText[rope], 0, length]; }; -- PutRope PutTime: PROC [stream: IO.STREAM, time: BasicTime.GMT _ BasicTime.nullGMT] = { PutLongWord[stream, LOOPHOLE[time, FourChars]]; }; -- PutTime PutBool: PROC[stream: IO.STREAM, bool: BOOL _ TRUE] = { stream.PutChar[LOOPHOLE[IF bool THEN 0C ELSE 1C, CHAR]]; }; -- PutBool PutByte: PROC [stream: IO.STREAM, byte: BYTE _ 0] = { stream.PutChar[LOOPHOLE[byte, CHAR]]; }; -- PutByte PutInt: PROC [stream: IO.STREAM, int: INT _ 0] = { PutLongWord[stream, LOOPHOLE[int, FourChars]]; }; -- PutInt PutReal: PROC [stream: IO.STREAM, real: REAL _ 0] = { PutLongWord[stream, LOOPHOLE[real, FourChars]]; }; -- PutReal PutCardinal: PROC [stream: IO.STREAM, word: CARDINAL _ 0] = { pair: CharPair _ LOOPHOLE[word, CharPair]; stream.PutChar[pair.high]; stream.PutChar[pair.low]; }; -- PutCardinal PutLongWord: PROC [stream: IO.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.