DIRECTORY Convert USING [RopeFromInt], Graph USING [AutoType, CaretIndex, CaretSpec, ColorIndex, Curve, EntityRec, EntityGroup, EntityGroupList, EntityGroupRec, EntityHashSize, CurveList, FontIndex, GraphColors, GraphFonts, GraphHandle, HashIndex, JustifX, JustifY, Mark, NestedEntities, NestedEntitiesList, NestedEntitiesRec, NtNan, 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, 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, groupId: INT] = { 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, TRUE, TRUE, TRUE]; -- use existing colors and fonts }; IF create THEN Unlock[newGraph]; }; -- NewGraph 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, text, paint]; 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, groupId: INT, values: ValueList, reverse, discard: BOOL _ TRUE] RETURNS [x: Curve _ 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]; x _ group.x; [x.oldValues, x.lastValue] _ IF reverse THEN ReverseValueList[values, discard] ELSE CopyValueList[values, discard]; InitSegEnd[x]; }; }; }; -- SetXValues AddCurve: PUBLIC PROC [handle: GraphHandle, groupId: INT, 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, values: ValueList _ NIL, reverse, discard: BOOL _ TRUE ] RETURNS [curve: Curve _ 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 _ NewCurve[handle, group, name, comment, colorIndex, mark, width]; IF values # NIL THEN { [curve.oldValues, curve.lastValue] _ IF reverse THEN ReverseValueList[values, discard] ELSE CopyValueList[values, discard]; InitSegAll[curve]; IF handle.chart.viewer # NIL THEN PaintEntity[handle, curve, TRUE, paint]; }; }; }; }; -- AddCurve NewCurve: PROC [handle: GraphHandle, group: EntityGroup, -- handle and group must not be nil. name, comment: ROPE, colorIndex: ColorIndex, mark: Mark, width: REAL ] RETURNS [curve: Curve _ 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]; }; -- NewCurve AppendValues: PUBLIC PROC [ handle: GraphHandle, groupId: INT, x: REAL, yvalues: ValueList, reverse, discard: BOOL _ TRUE] = { IF yvalues = NIL THEN BlinkMsg["y's missing."] 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 IF group.x = NIL THEN BlinkMsg["can't find x on the group."] ELSE { AppendToNE: PROC [tne: NestedEntities] = { FOR el: CurveList _ tne.entityList, el.rest UNTIL el = NIL DO newy: REAL _ IF v2temp = NIL THEN Graph.NtNan ELSE v2temp.first; oldy: REAL _ AppendY[el.first, newy, x1, x]; IF NOT first AND el.first.segments # NIL THEN { pv1 _ CONS[oldy, pv1]; pv2 _ CONS[newy, pv2]; pel _ CONS[el.first, pel]; }; v2temp _ v2temp.rest; ENDLOOP; FOR nel: NestedEntitiesList _ tne.children, nel.rest UNTIL nel = NIL DO AppendToNE[nel.first]; ENDLOOP; }; x1: REAL; pv1, pv2, v2, v2temp: ValueList _ NIL; pel: CurveList _ NIL; first: BOOL _ group.x.lastValue = NIL; IF NOT first THEN IF group.x.lastValue.first >= x THEN { BlinkMsg["Can't append x <= previous x."]; RETURN; }; x1 _ AppendX[group.x, x]; [v2, ] _ IF reverse THEN ReverseValueList[yvalues, discard] ELSE CopyValueList[yvalues, discard]; v2temp _ v2; AppendToNE[group.ys]; group.length _ group.length + 1; IF NOT first THEN PaintTails[handle, x1, x, pv1, pv2, pel, paint, TRUE]; }; }; }; -- AppendValues 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. þGraphOpsImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Last Edited: Sweetsun Chen, November 26, 1985 12:43:25 pm PST all arguments can be defaulted. If oldGraph is nil, this suggested name (or "Graph.graph", if it is empty) will be used; otherwise this suggested name will be used, if and only if the original file name for oldGraph is empty. IF NOT autoBounds THEN IF bounds.xmax <= bounds.xmin OR bounds.ymax <= bounds.ymin THEN BlinkMsg["xmax <= xmin or ymax <= ymin."]; IF NOT autoDivisions THEN IF divisions[x] < 1 OR divisions[y] < 1 THEN BlinkMsg["number of division < 2."]; Two methods to set x and y data: 1. SetXValues then AddCurve; or 2. call EnlistCurve for all curves, then AddCrossSection. all following arguments may be defaulted. If handle or values are nil then return nil without setting x values. If handle = nil or yvalues = nil then noop. Make sure that the number of y values agrees with the total number of curves in this group. And the order of values agrees with the order in which curves were added to the group by AddCurve, or its reverse if reverse is true. save currently plotted data (or all data on handle) to a file with handle.graph.fileName as name. msg is not nil if there is any problem saving the graph. Ê Ê˜J™Jšœ Ïmœ1™<šœ ™ Jšœ0™0—J˜šÏk ˜ Jšœžœ˜Jšœžœ¥žœNžœ˜†Jšœ žœ˜#Jšœ žœ˜Jšœ žœ˜˜ªJšœ žœÀ˜ÏJšœžœžœ˜Jšžœžœ˜Jšœžœ˜Jšœ žœ˜—J˜šœžœž˜Jšžœ;žœ˜UJšžœžœ*˜C—J˜Jšœ žœ˜%J™šÏnœž œ˜J™šœ3˜3JšœX™XJšœi™i—JšœÏc8˜FJšœ  K˜TJšœžœžœ˜Jšœžœžœ˜'Jšœ ˜9Jš œ žœžœžœžœžœ˜$Jšœžœžœžœžœžœžœ  ˜2Jš œ žœžœžœžœžœ˜+Jš œžœ žœ žœžœ˜1Jšœ žœžœ -˜FJšœžœ˜Jšœžœ˜Jšœžœ ˜:Jšœ žœžœ r˜ˆJšœžœ"žœ˜3J˜Jšœžœžœ˜Jšžœžœ-˜;šžœ˜Jšœ˜Jšžœ žœ1žœ˜GJ˜—šœžœ ˜šœžœ˜+šœžœ˜Jšœžœžœžœ˜-Jšœ˜—Jšœžœ:˜AJšœ( ˜BJšœ˜—Jšœžœ˜2Jšœžœ˜7Jš žœžœžœžœžœ˜€Jšœ˜Jšœ7žœžœ˜Iš žœžœžœžœžœžœ!˜XJ˜—šœ˜šžœž˜ Jšžœžœžœ ˜6—Jšžœ˜—Jšœ)˜)J˜&Jšžœžœžœ˜0Jš žœžœ žœžœžœžœ+™‚J˜Jš žœžœžœžœžœžœ%™kšžœžœ ž˜"Jšžœ žœžœ˜3Jšžœ˜—Jšœ˜š žœžœžœžœž˜Jšžœžœžœ˜6Jšžœ˜—J˜Jš œžœ žœžœžœ ˜@Jš œžœ žœžœžœ˜