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, AddTextButton, defaultColors, defaultFonts, PaintAll, PaintTails, PaintEntity, PaintText, ShowChart], 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]; GraphOpsImpl: CEDAR PROGRAM IMPORTS Convert, GraphCleanUp, GraphOps, GraphPrivate, GraphUtil, IO, Rope EXPORTS GraphOps = { OPEN Graph, GraphOps, GraphPrivate, GraphUtil; 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[] ELSE newGraph _ oldGraph; Lock[newGraph]; IF replace AND NOT create 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. ]]; 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]]; graph.fileName _ IF oldGraph = NIL THEN IF fileName.IsEmpty[] THEN "Graph.graph" ELSE fileName ELSE graph.fileName; graph.auto _ [autoDivisions, autoBounds]; graph.bounds _ bounds; IF NOT autoBounds THEN IF bounds.xmax <= bounds.xmin OR bounds.ymax <= bounds.ymin THEN BlinkMsg["xmax <= xmin or ymax <= ymin."]; graph.division _ divisions; IF NOT autoDivisions THEN IF divisions[x] < 2 OR divisions[y] < 2 THEN BlinkMsg["number of division < 2."]; 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] ELSE PaintAll[newGraph]; }; 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[] OR handle = NIL THEN BlinkMsg["Rope is empty or handle is nil."] 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 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 handle = NIL AND values = NIL THEN BlinkMsg["handle or x values are nil."] 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["handle is nil."] 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["handle is nil."] 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; curve _ NEW[EntityRec _ [ name: name, comment: comment, colorIndex: colorIndex, mark: mark, width: width, oldValues: NIL, group: group, parent: NIL, 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]]; IF group.ys = NIL THEN group.ys _ NEW[NestedEntitiesRec _ []]; group.ys.entityList _ AppendEntityList[group.ys.entityList, CONS[curve, NIL]]; graph.entityList _ AppendEntityList[graph.entityList, CONS[curve, NIL]]; IF handle.controller # NIL THEN AddEntityButton[handle, curve]; }; -- NewEntity AddCrossSection: PUBLIC PROC [ handle: GraphHandle, x: REAL, yvalues: ValueList, groupId: INT _ 0, reverse, discard: BOOL _ TRUE] = { IF handle = NIL OR yvalues = NIL THEN BlinkMsg["handle = NIL or yvalues = NIL."] 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; }; 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]; 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 }. 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 17, 1985 2:48:56 am PST all arguments can be defaulted. If fileName is empty, default name will depend on whether oldGraph is nil. If oldGraph is nil, "Graph.graph" will be used, otherwise the original fileName on handle retains. Two methods to set x and y data: 1. SetXValues then AddCurve; or 2. call EnlistEntity for all curves, then AddCrossSection. To ensure data integrity, it's better to lock/unlock the handle before/after calling the following procs. all following arguments may be defaulted. If handle or values are nil then return nil without setting x values. If handle = nil, or if number of values # number of x values, then return nil and no curve is created. same as AddCurve except that value list is nil. If handle = nil or yvalues = nil then noop. [firstx, x1] _ AppendX[xEntity, x]; [firsty, oldy] _ AppendY[el.first, tempVL2.first, x1, x]; v1, v2 are in the correct order. Ê æ˜J™Jšœ Ïmœ1™<šœ ™ Jšœ/™/—J˜šÏk ˜ Jšœžœ˜JšœžœŒžœNžœ˜íJšœ žœ˜#Jšœ žœ˜Jšœ žœx˜ŠJšœ žœÌ˜ÛJšœžœžœ˜Jšžœžœ˜Jšœžœ˜—J˜šœžœž˜Jšžœ;žœ˜JJšžœžœ*˜C—J˜šÏnœž œ˜J™šœ3˜3Jšœ¯™¯—JšœÏc8˜FJšœ  K˜TJšœžœžœ˜Jšœžœžœ˜'Jšœ ˜9Jš œ žœžœžœžœžœ˜$Jšœžœžœžœžœžœžœ  ˜2Jš œ žœžœžœžœžœ˜+Jš œžœ žœ žœžœ˜1Jšœ žœžœ -˜FJšœžœ˜Jšœžœ˜Jšœžœ ˜:Jšœ žœžœ r˜ˆJšœžœžœ žœ ˜=J˜Jšœžœžœ˜Jšžœžœžœ˜CJ˜Jš žœ žœžœžœ1žœ˜Všœžœ ˜šœžœ˜+šœžœ˜Jšœžœžœžœ˜-Jšœ˜—Jšœžœ:˜AJšœ( ˜BJšœ˜—Jš žœžœžœžœžœ˜€Jšœ˜šœ7žœžœ˜IJ˜—šœ˜Jš žœ žœžœžœžœžœ ˜MJšžœ˜—Jšœ)˜)J˜Jš žœžœ žœžœžœžœ+˜‚J˜Jš žœžœžœžœžœžœ%˜kšžœžœ ž˜"Jšžœ žœžœ˜3Jšžœ˜—Jšœ˜š žœžœžœžœž˜Jšžœžœžœ˜6Jšžœ˜—J˜Jš œžœ žœžœžœ ˜@Jš œžœ žœžœžœ˜Jšœ<žœžœ˜NJšœ6žœžœ˜HJšžœžœžœ ˜?Jšœ  ˜—J˜—šŸœžœžœ˜Jšœžœ˜1Jšœ žœžœžœ˜4J˜Jšœ+™+Jš žœ žœžœ žœžœ+˜Pšžœ˜Jšœ2˜2šžœ žœžœ ˜JšœK˜K—šžœžœBž˜Mšœ žœ8˜CJšžœ˜Jšžœ1˜3Jšœ˜——šžœžœ˜Jšœ˜Jšžœ žœžœ5˜Jšžœ˜Jšœžœ˜ Jšœžœ˜ Jšœžœžœ˜&š žœžœžœžœžœ˜8J˜bJšžœ˜J˜Jšœ#™#—J˜Jšœ˜šœ žœ žœ#˜;Jšžœ!˜%—Jšœ ˜ šžœ,žœžœž˜@šœžœ*˜4Jšœ9™9—Jšžœžœžœžœ ˜&Jšœ˜Jšžœ˜—Jšœ ˜ Jšœžœ˜$šžœžœžœ*žœ˜BJšœ ™ —J˜—J˜—J˜—Jšœ ˜—J˜J˜šžœ˜J˜0J˜—J™—…—%3ú