<> <> <> <> DIRECTORY Convert USING [RopeFromInt], Graph USING [AutoType, CaretIndex, CaretSpec, ColorIndex, Entity, EntityRec, EntityGroup, EntityGroupList, EntityGroupRec, EntityHashSize, EntityList, FontIndex, GraphColors, GraphFonts, GraphHandle, HashIndex, JustifX, JustifY, Mark, NestedEntities, NestedEntitiesRec, NullBox, ROPE, SegmentDataRec, TargetSpec, Text, Texts, TextRec, UnitLineWidth, ValueList, XY], GraphCleanUp USING [CleanUpHandle], GraphOps USING [Lock, Unlock], GraphPrivate USING [AddEntityButton, AddGroupButton, AddTextButton, defaultColors, defaultFonts, PaintAll, PaintTails, PaintEntity, PaintText, ShowChart, WriteGraphFile], GraphUtil USING [AppendEGL, AppendEntityList, AppendTexts, AppendX, AppendY, BlinkMsg, CopyValueList, InitSegAll, InitSegEnd, LengthOfEL, LengthOfVL, NewTextId, NewEntityId, NewGroupId, ReverseValueList, VanillaHandle], Imager USING [Box, VEC], IO USING [int, PutFR], Rope USING [Concat, IsEmpty], ViewerOps USING [PaintViewer]; GraphOpsImpl: CEDAR PROGRAM IMPORTS Convert, GraphCleanUp, GraphOps, GraphPrivate, GraphUtil, IO, Rope, ViewerOps EXPORTS GraphOps = { OPEN Graph, GraphOps, GraphPrivate, GraphUtil; handleIsNil: ROPE = "handle is nil."; <<>> NewGraph: PUBLIC PROC [ <> fileName, -- suggested file name to save the graph. <> <> groupName, -- name of the group of curves in this graph being created. comment, -- will show up on graph table following the name, but not on graph viewer. xName: ROPE _ NIL, autoBounds, autoDivisions: BOOL _ TRUE, bounds: Imager.Box _ NullBox, -- [xmin, ymin, xmax, ymax] divisions: ARRAY XY OF INT _ ALL[5], grids: ARRAY XY OF BOOL _ ALL[FALSE], -- true: on. targets: ARRAY XY OF TargetSpec _ ALL[NIL], carets: ARRAY CaretIndex OF CaretSpec _ ALL[NIL], showSlope: BOOL _ FALSE, -- slope between primary and secondary carets colors: GraphColors _ NIL, fonts: GraphFonts _ NIL, oldGraph: GraphHandle _ NIL, -- Create new viewer iff nil. replace: BOOL _ FALSE -- true: remove all existing curves and texts on old handle; false: merge new curves and texts with existing ones. ] RETURNS [newGraph: GraphHandle _ NIL, groupId: INT _ 0] = { create: BOOL _ oldGraph = NIL; IF create THEN {newGraph _ VanillaHandle[]; Lock[newGraph]} ELSE { newGraph _ oldGraph; IF replace THEN newGraph _ GraphCleanUp.CleanUpHandle[newGraph, FALSE]; }; { OPEN newGraph; group: EntityGroup _ NEW[EntityGroupRec _ [ x: NEW[EntityRec _ [ name: IF xName.IsEmpty[] THEN "X" ELSE xName, id: NewEntityId[newGraph, 0]]], ys: NEW[NestedEntitiesRec _ [name: groupName, comment: comment]], id: (groupId _ NewGroupId[newGraph, 0]) -- length to be set later. ]]; hIndex: HashIndex _ group.x.id MOD EntityHashSize; entityHash[hIndex] _ CONS[group.x, entityHash[hIndex]]; IF group.ys.name.IsEmpty[] AND group.ys.comment.IsEmpty[] THEN group.ys.comment _ IO.PutFR["Curves group %g", IO.int[group.id]]; group.x.group _ group; entityGroupList _ GraphUtil.AppendEGL[entityGroupList, CONS[group, NIL]]; IF controller # NIL THEN IF controller.table # NIL THEN AddGroupButton[newGraph, group]; graph.fileName _ IF graph.fileName.IsEmpty[] THEN IF fileName.IsEmpty[] THEN "Graph.graph" ELSE fileName ELSE graph.fileName; graph.auto _ [autoDivisions, autoBounds]; boundsToMerge _ graph.bounds _ bounds; IF graph.auto[bounds] THEN mergingBounds _ TRUE; <> graph.division _ divisions; <> FOR i: CaretIndex IN CaretIndex DO IF carets[i] # NIL THEN graph.caret[i] _ carets[i]; ENDLOOP; graph.showSlope _ showSlope; FOR i: XY IN XY DO IF targets[i] # NIL THEN graph.target[i] _ targets[i]; ENDLOOP; graph.grids _ grids; graph.color^ _ IF colors = NIL THEN defaultColors^ ELSE colors^; graph.font^ _ IF fonts = NIL THEN defaultFonts^ ELSE fonts^; IF chart.viewer = NIL THEN ShowChart[newGraph] -- will init colors and fonts ELSE PaintAll[newGraph]; -- use existing colors and fonts }; IF create THEN Unlock[newGraph]; }; -- NewGraph <> <<1. SetXValues then AddCurve; or>> <<2. call EnlistEntity for all curves, then AddCrossSection.>> <<>> AddText: PUBLIC PROC [handle: GraphHandle, rope: ROPE, <> place: Imager.VEC _ [0.0, 0.0], -- location with respect to origion and relative to size of axes box fontIndex: FontIndex _ 0, colorIndex: ColorIndex _ 15, -- black rotation: REAL _ 0.0, -- angle of rotation, ccw, in degrees. justifX: JustifX _ left, justifY: JustifY _ bottom ] RETURNS [text: Text _ NIL]= { IF rope.IsEmpty[] THEN BlinkMsg["Rope is nil."] ELSE IF handle = NIL THEN BlinkMsg[handleIsNil] ELSE { OPEN handle; text _ NEW[TextRec _ [ text: rope, place: place, fontIndex: fontIndex, colorIndex: colorIndex, rotation: rotation, justifX: justifX, justifY: justifY, id: NewTextId[handle, 0] ]]; allTexts _ AppendTexts[allTexts, CONS[text, NIL]]; graph.texts _ AppendTexts[graph.texts, CONS[text, NIL]]; IF chart.viewer # NIL THEN PaintText[handle, paint, text]; IF controller # NIL THEN IF controller.table # NIL THEN AddTextButton[handle, text]; }; }; -- AddText GroupFromId: PROC [handle: GraphHandle, id: INT] RETURNS [eg: EntityGroup _ NIL] = { FOR egl: EntityGroupList _ handle.entityGroupList, egl.rest UNTIL egl = NIL DO IF egl.first.id = id THEN eg _ egl.first; ENDLOOP; }; -- GroupFromId SetXValues: PUBLIC PROC [handle: GraphHandle _ NIL, values: ValueList, groupId: INT _ 0, reverse, discard: BOOL _ TRUE] RETURNS [xEntity: Entity _ NIL]= { <> IF values = NIL THEN BlinkMsg["no x values."] ELSE IF handle = NIL THEN BlinkMsg[handleIsNil] ELSE { OPEN handle; group: EntityGroup _ GroupFromId[handle, groupId]; IF group = NIL THEN BlinkMsg[ Rope.Concat["Can't find a group with id = ", Convert.RopeFromInt[groupId]]] ELSE { group.length _ LengthOfVL[values]; xEntity _ group.x; [xEntity.oldValues, xEntity.lastValue] _ IF reverse THEN ReverseValueList[values, discard] ELSE CopyValueList[values, discard]; InitSegEnd[xEntity]; }; }; }; -- SetXValues AddCurve: PUBLIC PROC [handle: GraphHandle, values: ValueList, groupId: INT _ 0, name, comment: ROPE _ NIL, colorIndex: ColorIndex _ 0, -- If this argument is zero, a nonzero color index will be assigned for this curve automatically when it is plotted. mark: Mark _ none, width: REAL _ UnitLineWidth, reverse, discard: BOOL _ TRUE ] RETURNS [curve: Entity _ NIL]= { <> IF handle = NIL THEN BlinkMsg[handleIsNil] ELSE { OPEN handle; group: EntityGroup _ GroupFromId[handle, groupId]; IF group = NIL THEN BlinkMsg[ Rope.Concat["Can't find a group with id = ", Convert.RopeFromInt[groupId]]] ELSE IF LengthOfVL[values] # group.length THEN BlinkMsg["number of y values # number of x values."] ELSE { curve _ NewEntity[handle, group, name, comment, colorIndex, mark, width]; [curve.oldValues, curve.lastValue] _ IF reverse THEN ReverseValueList[values, discard] ELSE CopyValueList[values, discard]; InitSegAll[curve]; IF handle.chart.viewer # NIL THEN PaintEntity[handle, paint, curve]; }; }; }; -- AddCurve EnlistEntity: PUBLIC PROC [handle: GraphHandle, groupId: INT _ 0, name, comment: ROPE _ NIL, colorIndex: ColorIndex _ 0, -- If this argument is zero, a nonzero color index will be assigned for this curve automatically when it is plotted. mark: Mark _ none, width: REAL _ UnitLineWidth ] RETURNS [curve: Entity _ NIL] = { <> IF handle = NIL THEN BlinkMsg[handleIsNil] ELSE { group: EntityGroup _ GroupFromId[handle, groupId]; IF group = NIL THEN BlinkMsg[ Rope.Concat["Can't find a group with id = ", Convert.RopeFromInt[groupId]]] ELSE curve _ NewEntity[handle, group, name, comment, colorIndex, mark, width]; }; }; -- EnlistEntity NewEntity: PROC [handle: GraphHandle, group: EntityGroup, -- handle and group must not be nil. name, comment: ROPE, colorIndex: ColorIndex, mark: Mark, width: REAL ] RETURNS [curve: Entity _ NIL] = { OPEN handle; hashIndex: HashIndex; IF group.ys = NIL THEN {BlinkMsg["group.ys = nil."]; RETURN}; curve _ NEW[EntityRec _ [ name: name, comment: comment, colorIndex: colorIndex, mark: mark, width: width, oldValues: NIL, group: group, parent: group.ys, id: NewEntityId[handle, 1] ]]; IF name.IsEmpty[] AND comment.IsEmpty[] THEN curve.name _ IO.PutFR["Curve %g", IO.int[curve.id]]; hashIndex _ curve.id MOD EntityHashSize; entityHash[hashIndex] _ CONS[curve, entityHash[hashIndex]]; group.ys.entityList _ AppendEntityList[group.ys.entityList, CONS[curve, NIL]]; graph.entityList _ AppendEntityList[graph.entityList, CONS[curve, NIL]]; IF handle.controller # NIL THEN IF handle.controller.table # NIL THEN AddEntityButton[handle, curve]; }; -- NewEntity AddCrossSection: PUBLIC PROC [ handle: GraphHandle, x: REAL, yvalues: ValueList, groupId: INT _ 0, reverse, discard: BOOL _ TRUE] = { <> IF yvalues = NIL THEN BlinkMsg["no y values."] ELSE IF handle = NIL THEN BlinkMsg[handleIsNil] ELSE { group: EntityGroup _ GroupFromId[handle, groupId]; IF group = NIL THEN BlinkMsg[ Rope.Concat["Can't find a group with id = ", Convert.RopeFromInt[groupId]]] ELSE IF LengthOfVL[yvalues] # LengthOfEL[handle.graph.entityList, group] THEN BlinkMsg[IO.PutFR["Number of values (%g) # number of curves (%g).", IO.int[LengthOfVL[yvalues]], IO.int[LengthOfEL[handle.graph.entityList, group]], ]] ELSE { OPEN handle; xEntity: Entity _ group.x; IF xEntity = NIL THEN BlinkMsg["x and/or y entities not on the list yet."] ELSE { x1: REAL; v1, v2, v2temp: ValueList _ NIL; first: BOOL _ xEntity.lastValue = NIL; IF NOT first THEN IF xEntity.lastValue.first >= x THEN { BlinkMsg["Sorry, cross sections must be added with their x values in strictly increasing order."]; RETURN; }; <<[firstx, x1] _ AppendX[xEntity, x];>> x1 _ AppendX[xEntity, x]; [v2, ] _ IF reverse THEN ReverseValueList[yvalues, discard] ELSE CopyValueList[yvalues, discard]; v2temp _ v2; FOR el: EntityList _ graph.entityList, el.rest UNTIL el = NIL DO oldy: REAL _ AppendY[el.first, v2temp.first, x1, x]; <<[firsty, oldy] _ AppendY[el.first, tempVL2.first, x1, x];>> IF NOT first THEN v1 _ CONS[oldy, v1]; v2temp _ v2temp.rest; ENDLOOP; group.length _ group.length + 1; [v1, ] _ ReverseValueList[v1, TRUE]; IF NOT first THEN PaintTails[handle, paint, v1, v2, x1, x, FALSE]; <> }; }; }; }; -- AddCrossSection SaveGraph: PUBLIC PROC[handle: GraphHandle, plottedOnly: BOOL _ TRUE] RETURNS [msg: ROPE _ NIL] = { <> <> IF handle = NIL THEN BlinkMsg[handleIsNil] ELSE { newName: ROPE; [msg, newName] _ WriteGraphFile[handle, handle.graph.fileName, plottedOnly]; IF msg = NIL AND handle.chart.viewer # NIL THEN { handle.chart.viewer.name _ newName; ViewerOps.PaintViewer[handle.chart.viewer, caption, FALSE, NIL]; }; }; }; -- SaveGraph }. LOG. SChen, October 26, 1985 9:26:57 pm PDT, created. <<>>