GraphFile.mesa, Copyright © 1985 by Xerox Corporation. All rights reserved.
Last Edited by:
Sweetsun Chen, October 22, 1985 9:37:23 pm PDT
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: ROPENIL];
SyntaxError: SIGNAL[message: ROPENIL];
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;
scratch for 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 <files>"
ELSE {
wDir: ROPE ← FileNames.CurrentWorkingDirectory[];
nGraphs: NAT ← 0;
maxGraphs: NAT ← 12;
allVersions: BOOLFALSE;
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: ROPENIL, allVersions: BOOLFALSE] RETURNS [fileList: RopeList ← NIL] = {
root, lastRoot: ROPENIL;
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: ROPENIL] RETURNS [handle: GraphHandle ← NIL, msg: ROPENIL] = {
handle ← VanillaHandle[];
msg ← GetGraph[handle, file];
IF msg = NIL THEN ShowChart[handle];
}; -- HandleFromFile
GetGraph: PUBLIC PROC [handle: GraphHandle ← NIL, file: ROPENIL] RETURNS [msg: ROPENIL] = { -- if no problem then return msg=nil.
IF HandleNotNil[handle] THEN {
s: IO.STREAMNIL;
ok: BOOLTRUE;
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: ROPENIL] = { OPEN handle;
nCurvesMax: CARDINAL;
xEntity: Entity ← NIL;
entityGroup: EntityGroup ← NIL;
entityList: EntityList ← NIL;
v1, v2: ValueList ← NIL;
x1, x2: REAL ← 0.0;
title, time, and file name.
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]]];
bounds, nCurvesMax, entityHash, and entityGroupList.
graph.bounds ← [GetReal[s], GetReal[s], GetReal[s], GetReal[s]];
nCurvesMax ← GetCardinal[s];
xEntity ← NEW[EntityRec ← [name: "X"]];
entityHash[0] ← CONS[xEntity, entityHash[0]];
graph.entityList ← CONS[xEntity, graph.entityList];
entityGroup ← NEW[EntityGroupRec ← [
x: xEntity,
ys: CONS[NEW[NestedEntitiesRec ← []], NIL] -- id = 0, should update length later.
]];
xEntity.group ← entityGroup;
entityGroupList ← CONS[entityGroup, 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.
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];
add tails
entityGroup.length ← GetCardinal[s];
FOR i: INT IN [1..entityGroup.length] DO
x2 ← GetReal[s];
xEntity.oldValues ← CONS[x2, xEntity.oldValues];
xEntity.newValues ← CONS[(paintInfo.x2 ← GetReal[s]), xEntity.newValues];
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 {
UNTIL axesRect.w > 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];
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
InitSegEnd[xEntity];
FOR el: EntityList ← graph.entityList, el.rest UNTIL el = NIL DO
InitSegAll[el.first];
ENDLOOP;
lastTextId ← 1;
lastEntityId ← nCurvesMax;
lastEntityGroupId is 0.
lastEntityColor ← ((nCurvesMax - 1) MOD LastEntityColor) + 1;
}; -- ReadPlotFile
ReadGraphFile: PROC [handle: GraphHandle, s: IO.STREAM] RETURNS [msg: ROPENIL] = {
OPEN handle;
egl: EntityGroupList;
graph.fileName ← NARROW[Atom.GetPropFromList[s.propList, $Name]];
The format could have been more compact. But it is easier to allow for upward/backward compatibility this way.
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];
AutoKey => 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;
entities on each entitylist have been put in the right order, cf., GetEntityList.
now resume the right order of values on all entities
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;
initialize segments for x entities.
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] = {
reads in graph.texts in the reversed order.
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: XYIF 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: XYIF 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: XYIF 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: XYIF 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;
reverse it.
FOR tegl: EntityGroupList ← egl, tegl.rest UNTIL tegl = NIL DO
handle.entityGroupList ← CONS[tegl.first, handle.entityGroupList];
ENDLOOP;
cleanup egl.
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;
UNTIL axesRect.w > 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: ROPENIL] = {
noop for now.
}; -- ReadDataFile
SaveGraph: PUBLIC PROC[viewer: Viewer, file: ROPENIL] RETURNS [msg: ROPENIL] = {
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 ANYNIL, file: ROPENIL] RETURNS[msg, newName: ROPENIL] = {
handle: GraphHandle ← NIL;
graph: GRAPHNIL;
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: BOOLTRUE;
s: IO.STREAM;
nVector: CARDINAL ← 0;
fileName: ROPEIF 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.";
ok ← FALSE;
CONTINUE };
PutRope[s, "binary graph"]; -- format id
PutRope[s, Atom.GetPropFromList[s.propList, $Name]];
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: BOOLFALSE;
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: BOOLFALSE] = {
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, ValuesKey]; PutValueList[s, entity.newValues];
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: BOOLFALSE] = {
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: BOOLFALSE] = {
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: VLLNIL;
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: VLLNIL] = { -- 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
copy to vll in the correct order.
FOR tvll ← vllR, tvll.rest UNTIL tvll = NIL DO
vll ← CONS[tvll.first, vll];
ENDLOOP;
clean up the reversed one.
vllR ← CleanUpVLL[vllR];
start to work.
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];
clean up.
vll ← CleanUpVLL[vll];
}; -- PutCrossSections
WriteDataFile: PUBLIC PROC [graph: GRAPHNIL, file: ROPENIL] RETURNS [msg: ROPENIL] = {
write text formatted graph file
}; -- WriteDataFile
reading routines
GetRope: PROC [stream: IO.STREAM] RETURNS [rope: ROPE] = {
length: NAT = GetCardinal[stream];
text: REF TEXTIF 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: BOOLFALSE] = {
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: ROPENIL] 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
writing routines
PutRope: PROC [stream: IO.STREAM, rope: ROPENIL] = {
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: BOOLTRUE] = {
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 <graph file list>, reviews the graphs in the files. See GraphDoc.tioga for more information."];
and for historical reasons
Commander.Register["Plot", GraphFiles, "Plot <graph file list>, 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.