DIRECTORY Convert USING [RopeFromInt], Graph USING [AutoType, CaretIndex, Entity, EntityList, NullVec, SegmentData, SegmentDataList, Text, Texts, ValueList, Viewer, XY], GraphCarets USING [Move, TurnOn, TurnOff, StartBlinking, StopBlinking], GraphCleanUp USING [CleanUpVL], GraphPrivate USING [FlipToggleOnPanel, GraphHandle, GraphPlaceProc, GraphProc, Lock, PaintAll, PaintGrids, PaintTarget, PaintText, RemoveText, RemoveEntity, Resume, ResumeEntityFields, ResumeTextFields, Unlock], GraphUtil USING [Almost, BlinkMsg, ChartPosToTextPos, CrossSegment, DataBounds, HandleNotNil, RealToScreenI, RectangleValid, TextRect, ScreenIToReal, ScreenToReal, ScreenIToRealVec, SetToggleColor, ViewerNotNil], Imager USING [Context, MaskStroke, PathProc, Rectangle, SetColor, SetStrokeWidth, VEC], ImagerBackdoor USING [invert], Process USING [Abort, CheckForAbort, Detach], Real USING [Float, LargestNumber], RealFns USING [SqRt], Rope USING [Cat], ViewerPrivate USING [Screen, PaintScreen, ViewerScreen], ViewerTools USING [SetContents]; GraphEdit: CEDAR PROGRAM IMPORTS Convert, GraphCarets, GraphCleanUp, GraphPrivate, GraphUtil, Imager, ImagerBackdoor, Process, Real, RealFns, Rope, ViewerPrivate, ViewerTools EXPORTS GraphPrivate = { OPEN Graph, GraphPrivate, GraphUtil; SwitchAuto: PUBLIC PROC[handle: GraphHandle _ NIL, type: AutoType _ bounds] = { IF HandleNotNil[handle] THEN { OPEN handle; graph.auto[type] _ ~graph.auto[type]; IF controller # NIL THEN IF graph.auto[type] # controller.auto[type] THEN FlipToggleOnPanel[handle, IF type = divisions THEN $AutoDiv ELSE $AutoBounds]; IF type = bounds AND graph.auto[bounds] THEN graph.bounds _ DataBounds[graph.entityList]; IF chart.viewer # NIL THEN PaintAll[handle]; }; }; -- SwitchAuto SwitchCaretVisibility: PUBLIC PROC[handle: GraphHandle _ NIL, index: CaretIndex _ primary] = { -- invoked by ctrl-1, ctrl-2, or ctrl-3 on chart, or menus on controller. IF HandleNotNil[handle] THEN { OPEN handle; IF chart.viewer = NIL THEN graph.caret[index].on _ ~graph.caret[index].on ELSE IF graph.caret[index].on THEN GraphCarets.TurnOff[handle, index] ELSE GraphCarets.TurnOn[handle, index]; IF controller # NIL THEN IF graph.caret[index].on # controller.caretOn[index] THEN FlipToggleOnPanel[handle, SELECT index FROM primary => $Primary, secondary => $Secondary, ENDCASE => $TextCaret]; }; }; -- SwitchCaretVisibility SwitchCaretBlinking: PUBLIC PROC[handle: GraphHandle _ NIL, index: CaretIndex _ primary] = { -- shift-1, shift-2, or shift-3 on chart, or menus on controller with shift down. IF HandleNotNil[handle] THEN { OPEN handle; IF chart.viewer = NIL THEN chart.caretState[index].toBlink _ ~chart.caretState[index].toBlink ELSE IF chart.caretState[index].toBlink THEN GraphCarets.StopBlinking[handle, index] ELSE GraphCarets.StartBlinking[handle, index]; IF handle.controller # NIL THEN IF graph.caret[index].on # controller.caretOn[index] THEN FlipToggleOnPanel[handle, SELECT index FROM primary => $Primary, secondary => $Secondary, ENDCASE => $TextCaret]; }; }; -- SwitchCaretBlinking SwitchTarget: PUBLIC PROC[handle: GraphHandle _ NIL, xy: XY _ x] = { IF HandleNotNil[handle] THEN { OPEN handle; graph.target[xy].on _ ~graph.target[xy].on; IF controller # NIL THEN IF graph.target[xy].on # controller.targetOn[xy] THEN FlipToggleOnPanel[handle, IF xy = x THEN $TargetXOn ELSE $TargetYOn]; IF chart.viewer # NIL THEN PaintTarget[handle, IF graph.target[xy].on THEN paint ELSE erase, xy]; }; }; -- SwitchTarget SwitchGrid: PUBLIC PROC[handle: GraphHandle _ NIL, xy: XY _ x] = { IF HandleNotNil[handle] THEN { OPEN handle; IF chart.viewer # NIL THEN PaintGrids[handle, erase, xy]; graph.grids[xy] _ ~graph.grids[xy]; IF controller # NIL THEN IF graph.grids[xy] # controller.gridOn[xy] THEN FlipToggleOnPanel[handle, IF xy = x THEN $GridX ELSE $GridY]; IF chart.viewer # NIL THEN PaintGrids[handle, paint, xy]; }; }; -- SwitchGrid SelectPoint: PUBLIC GraphPlaceProc = { IF HandleNotNil[handle] THEN IF ViewerNotNil[handle.chart.viewer] THEN { OPEN handle; graph.caret[primary]^ _ [on: TRUE, place: ScreenIToRealVec[handle, sx, sy]]; IF controller # NIL THEN { Resume[handle, $Primary]; Resume[handle, $Slope]; }; IF chart.viewer # NIL THEN GraphCarets.Move[handle, sx, sy, primary, TRUE]; }; }; -- Point MoveGraphCaret: PROC[handle: GraphHandle, index: CaretIndex, place: Imager.VEC, on: BOOL _ TRUE] = { -- place is in real coordinates. OPEN handle; graph.caret[index]^ _ [on: on, place: place]; IF controller # NIL THEN { Resume[handle, SELECT index FROM primary => $Primary, secondary => $Secondary ENDCASE => $TextCaret]; IF index # text THEN Resume[handle, $Slope]; }; IF chart.viewer # NIL THEN GraphCarets.Move[handle, RealToScreenI[handle, place.x, x], RealToScreenI[handle, place.y, y], index, on]; }; -- MoveGraphCaret ErasePrimary: PUBLIC GraphProc = { IF HandleNotNil[handle] THEN IF ViewerNotNil[handle.chart.viewer] THEN { OPEN handle; graph.caret[primary].on _ FALSE; IF controller # NIL THEN controller.caretOn[primary] _ SetToggleColor[controller.swCaret[primary], FALSE]; IF handle.chart.viewer # NIL THEN GraphCarets.TurnOff[handle, primary]; }; }; -- ErasePrimary MoveXhair2To1: PUBLIC GraphProc = { IF HandleNotNil[handle] THEN IF ViewerNotNil[handle.chart.viewer] THEN MoveGraphCaret[handle, secondary, handle.graph.caret[primary].place, TRUE]; }; -- MoveXhair2To1 SelectEntity: PUBLIC GraphPlaceProc = { IF HandleNotNil[handle] THEN IF ViewerNotNil[handle.chart.viewer] THEN { selectEntityProc: PROC [hd: GraphHandle, sx, sy: INTEGER, procId: INT] = { point: Imager.VEC; entity: Entity; [point, entity] _ ClosestPointAndEntity[hd, ScreenIToRealVec[hd, sx, sy], hd.graph.entityList, procId]; Lock[hd]; IF hd.selectProcId = procId AND entity # NIL THEN { MoveGraphCaret[hd, primary, point, TRUE]; hd.chart.selectedEntity _ entity; }; hd.selectProc _ NIL; Unlock[hd]; }; -- selectProc handle.selectProcId _ (handle.selectProcId + 1) MOD 500; TRUSTED { IF handle.selectProc # NIL THEN Process.Abort[handle.selectProc]; Process.Detach[handle.selectProc _ FORK selectEntityProc[handle, sx, sy, handle.selectProcId]]; }; }; }; -- SelectEntity ClosestPointOnEntity: PROC[from: Imager.VEC, entity: Entity] RETURNS [point: Imager.VEC _ NullVec, dist: REAL _ Real.LargestNumber] = { xseg: SegmentDataList _ entity.group.x.segments; IF entity # NIL THEN -- in case it is removed during waiting period in the process. IF entity.segments # NIL AND xseg # NIL THEN { FOR yseg: SegmentDataList _ entity.segments, yseg.rest UNTIL yseg.rest = NIL DO OPEN yseg.first; signedD: REAL _ from.x*nx + from.y*ny - d0; absD: REAL _ ABS[signedD]; IF absD < dist THEN { tx: REAL _ from.x - signedD*nx; ty: REAL _ from.y - signedD*ny; x1: REAL _ xseg.first.end; x2: REAL _ xseg.rest.first.end; y1: REAL _ end; y2: REAL _ yseg.rest.first.end; IF (tx - x1)*(tx - x2) <= 0 AND (ty - y1)*(ty - y2) <= 0 THEN { dist _ absD; point _ [tx, ty]; } ELSE { dx: REAL _ from.x - x1; dy: REAL _ from.y - y1; d1: REAL _ dx*dx + dy*dy; d2: REAL; d1Small: BOOL; dx _ from.x - x2; dy _ from.y - y2; d2 _ dx*dx + dy*dy; d1Small _ d1 < d2; IF (absD _ RealFns.SqRt[IF d1Small THEN d1 ELSE d2]) < dist THEN { dist _ absD; point _ IF d1Small THEN [x1, y1] ELSE [x2, y2]; }; }; }; xseg _ xseg.rest; ENDLOOP; }; }; -- ClosestPointOnEntity ClosestPointAndEntity: PROC[handle: GraphHandle, from: Imager.VEC, entityList: EntityList, id: INT] RETURNS [point: Imager.VEC, entity: Entity _ NIL] = { ENABLE ABORTED => {Unlock[handle]; CONTINUE}; minDist: REAL _ Real.LargestNumber; tDist: REAL; tPoint: Imager.VEC; FOR el: EntityList _ entityList, el.rest UNTIL el = NIL DO Lock[handle]; Process.CheckForAbort[]; IF handle.selectProcId # id THEN {Unlock[handle]; EXIT}; [tPoint, tDist] _ ClosestPointOnEntity[from, el.first]; IF tDist < minDist THEN { minDist _ tDist; entity _ el.first; point _ tPoint; }; Unlock[handle]; ENDLOOP; }; -- ClosestPointAndEntity EraseSelEntity: PUBLIC GraphProc = { IF ViewerNotNil[handle.chart.viewer] THEN RemoveEntity[handle, handle.chart.selectedEntity]; }; -- EraseSelEntity ShowSelEntityOnPanel: PUBLIC GraphProc = { IF HandleNotNil[handle] THEN IF ViewerNotNil[handle.chart.viewer] THEN { OPEN handle; entity: Entity _ chart.selectedEntity; IF entity # NIL AND controller # NIL THEN { ViewerTools.SetContents[controller.entityId, Convert.RopeFromInt[entity.id]]; ResumeEntityFields[handle, entity]; }; }; }; -- ShowSelEntityOnPanel JumpLeftAnyCurve: PUBLIC GraphProc = { IF HandleNotNil[handle] THEN IF ViewerNotNil[handle.chart.viewer] THEN { OPEN handle; entity: Entity _ NIL; from, newPlace: Imager.VEC; from _ graph.caret[primary].place; newPlace _ [-Real.LargestNumber, from.y]; FOR el: EntityList _ graph.entityList, el.rest UNTIL el = NIL DO newX: REAL; crossed: BOOL _ FALSE; [newX, crossed] _ LeftCrossOnEntity[from, el.first]; IF crossed THEN IF newX > newPlace.x AND NOT Almost[from.x, newX] THEN { newPlace.x _ newX; entity _ el.first}; ENDLOOP; IF entity # NIL THEN { MoveGraphCaret[handle, primary, newPlace, TRUE]; chart.selectedEntity _ entity; }; }; }; -- JumpLeftAnyCurve JumpRightAnyCurve: PUBLIC GraphProc = { IF HandleNotNil[handle] THEN IF ViewerNotNil[handle.chart.viewer] THEN { OPEN handle; entity: Entity _ NIL; from, newPlace: Imager.VEC; from _ graph.caret[primary].place; newPlace _ [Real.LargestNumber, from.y]; FOR el: EntityList _ graph.entityList, el.rest UNTIL el = NIL DO newX: REAL; crossed: BOOL _ FALSE; [newX, crossed] _ RightCrossOnEntity[from, el.first]; IF crossed THEN IF newX < newPlace.x AND NOT Almost[from.x, newX] THEN {newPlace.x _ newX; entity _ el.first}; ENDLOOP; IF entity # NIL THEN { MoveGraphCaret[handle, primary, newPlace, TRUE]; chart.selectedEntity _ entity; }; }; }; -- JumpRightAnyCurve LeftCrossOnSegment: PROC[from, vl, vr: Imager.VEC] RETURNS [crossX: REAL, crossed: BOOL _ FALSE] = { IF (vl.y - from.y)*(vr.y - from.y) <= 0 THEN { crossX _ IF vl.y = vr.y THEN MIN[from.x, vr.x] ELSE vl.x + (from.y - vl.y)/(vr.y - vl.y)*(vr.x - vl.x); crossed _ crossX <= from.x; }; }; -- LeftCrossOnSegment RightCrossOnSegment: PROC[from, vl, vr: Imager.VEC] RETURNS [crossX: REAL, crossed: BOOL _ FALSE] = { IF (vl.y - from.y)*(vr.y - from.y) <= 0 THEN { crossX _ IF vl.y = vr.y THEN MAX[from.x, vl.x] ELSE vl.x + (from.y - vl.y)/(vr.y - vl.y)*(vr.x - vl.x); crossed _ crossX >= from.x; }; }; -- RightCrossOnSegment LeftCrossOnEntity: PROC[from: Imager.VEC, entity: Entity, onIt: BOOL _ FALSE] RETURNS [newX: REAL, crossed: BOOL _ FALSE] = { -- all in terms of real coord. IF entity # NIL THEN { xvl, yvl, xvlSave, yvlSave: ValueList _ NIL; xSegs: SegmentDataList _ entity.group.x.segments; ySegs: SegmentDataList _ entity.segments; UNTIL xSegs = NIL OR ySegs = NIL DO tx: REAL _ xSegs.first.end; IF onIt THEN IF tx >= from.x THEN EXIT; xvl _ CONS[tx, xvl]; yvl _ CONS[ySegs.first.end, yvl]; IF NOT onIt THEN IF tx >= from.x THEN EXIT; xSegs _ xSegs.rest; ySegs _ ySegs.rest; ENDLOOP; xvlSave _ xvl; yvlSave _ yvl; UNTIL (crossed OR xvl = NIL) DO -- yvl must have the same length as xvl, see above. IF xvl.rest # NIL AND yvl.rest # NIL THEN [newX, crossed] _ LeftCrossOnSegment[from, [xvl.rest.first, yvl.rest.first], [xvl.first, yvl.first]]; xvl _ xvl.rest; yvl _ yvl.rest; ENDLOOP; xvlSave _ GraphCleanUp.CleanUpVL[xvlSave]; yvlSave _ GraphCleanUp.CleanUpVL[yvlSave]; }; }; -- LeftCrossOnEntity RightCrossOnEntity: PROC[from: Imager.VEC, entity: Entity, onIt: BOOL _ FALSE] RETURNS [newX: REAL, crossed: BOOL _ FALSE] = { IF entity # NIL THEN { xSegs: SegmentDataList _ entity.group.x.segments; ySegs: SegmentDataList _ entity.segments; UNTIL xSegs = NIL OR ySegs = NIL DO tx: REAL _ xSegs.first.end; IF NOT onIt THEN IF tx >= from.x THEN EXIT; xSegs _ xSegs.rest; ySegs _ ySegs.rest; IF onIt THEN IF tx >= from.x THEN EXIT; ENDLOOP; UNTIL (crossed OR xSegs = NIL OR ySegs = NIL) DO IF xSegs.rest # NIL AND ySegs.rest # NIL THEN [newX, crossed] _ RightCrossOnSegment[from, [xSegs.first.end, ySegs.first.end], [xSegs.rest.first.end, ySegs.rest.first.end]]; xSegs _ xSegs.rest; ySegs _ ySegs.rest; ENDLOOP; }; }; -- RightCrossOnEntity JumpUpAnyCurve: PUBLIC GraphProc = { IF HandleNotNil[handle] THEN IF ViewerNotNil[handle.chart.viewer] THEN { OPEN handle; from: Imager.VEC _ graph.caret[primary].place; to: Imager.VEC _ [from.x, Real.LargestNumber]; entity: Entity _ NIL; FOR el: EntityList _ graph.entityList, el.rest UNTIL el = NIL DO ok: BOOL; vl, vr: Imager.VEC; [ok, vl, vr] _ CrossSegment[el.first.group.x.segments, el.first.segments, from.x]; IF ok THEN { y: REAL _ IF vr.x = vl.x THEN MAX[from.y, MIN[vl.y, vr.y]] ELSE vl.y + (vr.y - vl.y)/(vr.x - vl.x)*(from.x - vl.x); IF y > from.y AND y < to.y THEN IF NOT Almost[to.y, from.y] THEN { entity _ el.first; to.y _ y; }; }; ENDLOOP; IF entity # NIL THEN { MoveGraphCaret[handle, primary, to, TRUE]; chart.selectedEntity _ entity; }; }; }; -- JumpUpAnyCurve JumpDownAnyCurve: PUBLIC GraphProc = { IF HandleNotNil[handle] THEN IF ViewerNotNil[handle.chart.viewer] THEN { OPEN handle; from: Imager.VEC _ graph.caret[primary].place; to: Imager.VEC _ [from.x, -Real.LargestNumber]; entity: Entity _ NIL; FOR el: EntityList _ graph.entityList, el.rest UNTIL el = NIL DO ok: BOOL; vl, vr: Imager.VEC; [ok, vl, vr] _ CrossSegment[el.first.group.x.segments, el.first.segments, from.x]; IF ok THEN { y: REAL _ IF vr.x = vl.x THEN MIN[from.y, MAX[vl.y, vr.y]] ELSE vl.y + (vr.y - vl.y)/(vr.x - vl.x)*(from.x - vl.x); IF y < from.y AND y > to.y THEN IF NOT Almost[to.y, from.y] THEN { entity _ el.first; to.y _ y; }; }; ENDLOOP; IF entity # NIL THEN { MoveGraphCaret[handle, primary, to, TRUE]; chart.selectedEntity _ entity; }; }; }; -- JumpDownAnyCurve SlideOnSameCurve: PUBLIC GraphPlaceProc = { IF HandleNotNil[handle] THEN IF ViewerNotNil[handle.chart.viewer] THEN { OPEN handle; IF chart.selectedEntity # NIL THEN { point: Imager.VEC; [point, ] _ ClosestPointOnEntity[ [ScreenIToReal[handle, sx, x], ScreenIToReal[handle, sy, y]], chart.selectedEntity]; MoveGraphCaret[handle, primary, point, TRUE]; }; }; }; -- SlideOnSameCurve SlideLeftNSteps: PUBLIC PROC[handle: GraphHandle _ NIL, nSteps: INT _ 0] = { IF HandleNotNil[handle] THEN IF ViewerNotNil[handle.chart.viewer] THEN { OPEN handle; IF chart.selectedEntity # NIL THEN { loc: Imager.VEC _ graph.caret[primary].place; xseg: SegmentDataList _ chart.selectedEntity.group.x.segments; yseg: SegmentDataList _ chart.selectedEntity.segments; reversedX, reversedY: ValueList _ NIL; n: INT _ 0; FOR xseg _ xseg, xseg.rest UNTIL xseg = NIL DO IF xseg.first.end >= loc.x THEN EXIT; reversedX _ CONS[xseg.first.end, reversedX]; reversedY _ CONS[yseg.first.end, reversedY]; yseg _ yseg.rest; ENDLOOP; UNTIL n >= nSteps DO IF reversedX = NIL THEN { BlinkMsg[Rope.Cat[ "Only ", Convert.RopeFromInt[n], " steps are moved."]]; EXIT; }; loc _ [reversedX.first, reversedY.first]; IF chart.viewer # NIL THEN GraphCarets.Move[handle, RealToScreenI[handle, loc.x, x], RealToScreenI[handle, loc.y, y], primary, TRUE]; n _ n + 1; reversedX _ reversedX.rest; reversedY _ reversedY.rest; ENDLOOP; graph.caret[primary]^ _ [on: TRUE, place: loc]; IF controller # NIL THEN { Resume[handle, $Primary]; Resume[handle, $Slope]; }; }; }; }; -- SlideLeftNSteps SlideRightNSteps: PUBLIC PROC[handle: GraphHandle _ NIL, nSteps: INT _ 0] = { IF HandleNotNil[handle] THEN IF ViewerNotNil[handle.chart.viewer] THEN { OPEN handle; IF chart.selectedEntity # NIL THEN { loc: Imager.VEC _ graph.caret[primary].place; xseg: SegmentDataList _ chart.selectedEntity.group.x.segments; yseg: SegmentDataList _ chart.selectedEntity.segments; n: INT _ 0; FOR xseg _ xseg, xseg.rest UNTIL xseg = NIL DO IF xseg.first.end > loc.x THEN EXIT; ENDLOOP; FOR xseg _ xseg, xseg.rest UNTIL n >= nSteps DO IF xseg = NIL THEN { BlinkMsg[Rope.Cat[ "Only ", Convert.RopeFromInt[n], " steps are moved."]]; EXIT; }; loc _ [xseg.first.end, yseg.first.end]; IF chart.viewer # NIL THEN GraphCarets.Move[handle, RealToScreenI[handle, loc.x, x], RealToScreenI[handle, loc.y, y], primary, TRUE]; n _ n + 1; yseg _ yseg.rest; ENDLOOP; graph.caret[primary]^ _ [on: TRUE, place: loc]; IF controller # NIL THEN { Resume[handle, $Primary]; Resume[handle, $Slope]; }; }; }; }; -- SlideRightNSteps JumpLeftSameCurve: PUBLIC GraphProc = { IF HandleNotNil[handle] THEN IF ViewerNotNil[handle.chart.viewer] THEN { OPEN handle; found: BOOL; oldX: REAL _ graph.caret[primary].place.x; oldY: REAL _ graph.caret[primary].place.y; newX: REAL; [newX, found] _ LeftCrossOnEntity[[oldX, oldY], chart.selectedEntity, TRUE]; IF found THEN IF NOT Almost[newX, oldX] THEN MoveGraphCaret[handle, primary, [newX, oldY], TRUE]; }; }; -- JumpLeftSameCurve JumpRightSameCurve: PUBLIC GraphProc = { IF HandleNotNil[handle] THEN IF ViewerNotNil[handle.chart.viewer] THEN { OPEN handle; found: BOOL; oldX: REAL _ graph.caret[primary].place.x; oldY: REAL _ graph.caret[primary].place.y; newX: REAL; [newX, found] _ RightCrossOnEntity[[oldX, oldY], chart.selectedEntity]; IF found THEN IF NOT Almost[newX, oldX] THEN MoveGraphCaret[handle, primary, [newX, oldY], TRUE]; }; }; -- JumpRightSameCurve SelectText: PUBLIC GraphPlaceProc = { IF HandleNotNil[handle] THEN IF ViewerNotNil[handle.chart.viewer] THEN { OPEN handle; IF RectangleValid[axesRect] THEN { gText: Text _ NIL; hp: Imager.VEC; -- hot point FOR t: Texts _ graph.texts, t.rest UNTIL t = NIL DO rect: Imager.Rectangle; [rect, hp] _ TextRect[t.first, imagerFonts[screen][t.first.fontIndex], axesRect]; IF (rect.x - sx)*(rect.x + rect.w - sx) <= 0.0 AND (rect.y - sy)*(rect.y + rect.h - sy) <= 0.0 THEN {gText _ t.first; EXIT}; ENDLOOP; IF gText # NIL THEN { MoveGraphCaret[handle, text, [ScreenToReal[handle, hp.x, x], ScreenToReal[handle, hp.y, y]], TRUE]; chart.selectedText _ gText; }; }; }; }; -- SelectText EraseSelText: PUBLIC GraphProc = { IF ViewerNotNil[handle.chart.viewer] THEN RemoveText[handle, handle.chart.selectedText]; }; -- EraseSelText MoveSelText: PUBLIC GraphPlaceProc = { IF HandleNotNil[handle] THEN IF ViewerNotNil[handle.chart.viewer] THEN { OPEN handle; text: Text _ chart.selectedText; IF text # NIL THEN { MoveGraphCaret[handle, text, ScreenIToRealVec[handle, sx, sy], TRUE]; PaintText[handle, erase, text]; text.place _ ChartPosToTextPos[axesRect, [Real.Float[sx], Real.Float[sy]]]; PaintText[handle, paint, text]; }; }; }; -- MoveSelText ShowSelTextOnPanel: PUBLIC GraphProc = { IF HandleNotNil[handle] THEN { OPEN handle; text: Text _ chart.selectedText; IF text # NIL AND controller # NIL THEN { ViewerTools.SetContents[controller.textId, Convert.RopeFromInt[text.id]]; ResumeTextFields[handle, text]; }; }; }; -- ShowSelectedTextOnPanel HalfZoomInRatio: REAL = 0.707*0.5; HalfZoomOutRatio: REAL = 1.414*0.5; ZoomIn: PUBLIC GraphPlaceProc = { IF HandleNotNil[handle] THEN IF ViewerNotNil[handle.chart.viewer] THEN { OPEN handle; center: Imager.VEC _ ScreenIToRealVec[handle, sx, sy]; halfW: REAL _ HalfZoomInRatio*realRect.w; halfH: REAL _ HalfZoomInRatio*realRect.h; graph.bounds _ [xmin: center.x - halfW, ymin: center.y - halfH, xmax: center.x + halfW, ymax: center.y + halfH]; graph.auto[bounds] _ FALSE; PaintAll[handle]; }; }; -- ZoomIn ZoomOut: PUBLIC GraphPlaceProc = { IF HandleNotNil[handle] THEN IF ViewerNotNil[handle.chart.viewer] THEN { OPEN handle; center: Imager.VEC _ ScreenIToRealVec[handle, sx, sy]; halfW: REAL _ HalfZoomOutRatio*realRect.w; halfH: REAL _ HalfZoomOutRatio*realRect.h; graph.bounds _ [xmin: center.x - halfW, ymin: center.y - halfH, xmax: center.x + halfW, ymax: center.y + halfH]; graph.auto[bounds] _ FALSE; PaintAll[handle]; }; }; -- ZoomOut ZoomPoint: PUBLIC GraphPlaceProc = { IF HandleNotNil[handle] THEN IF ViewerNotNil[handle.chart.viewer] THEN { OPEN handle; IF zoomPt1.set THEN { viewer: Viewer _ chart.viewer; screen: ViewerPrivate.Screen _ ViewerPrivate.ViewerScreen[viewer]; DrawZBox: PROC [context: Imager.Context] = {DrawZoomBox[context, handle]}; IF zoomPt2.set THEN ViewerPrivate.PaintScreen[screen: screen, action: DrawZBox, suspendCarets: FALSE]; zoomPt2 _ [TRUE, sx, sy]; ViewerPrivate.PaintScreen[screen: screen, action: DrawZBox, suspendCarets: FALSE]; } ELSE zoomPt1 _ [TRUE, sx, sy]; }; }; -- ZoomPoint DrawZoomBox: PROC [context: Imager.Context, handle: GraphHandle] = { OPEN handle; path: Imager.PathProc = { moveTo[[zoomPt1.x, zoomPt1.y]]; lineTo[[zoomPt2.x, zoomPt1.y]]; lineTo[[zoomPt2.x, zoomPt2.y]]; lineTo[[zoomPt1.x, zoomPt2.y]]; }; context.SetStrokeWidth[1.0]; context.SetColor[ImagerBackdoor.invert]; context.MaskStroke[path, TRUE]; }; -- DrawZoomBox ZoomWithZoomPts: PUBLIC GraphProc = { IF HandleNotNil[handle] THEN IF ViewerNotNil[handle.chart.viewer] THEN { OPEN handle; x1, x2, y1, y2: REAL; x1 _ ScreenToReal[handle, zoomPt1.x, x]; x2 _ ScreenToReal[handle, zoomPt2.x, x]; y1 _ ScreenToReal[handle, zoomPt1.y, y]; y2 _ ScreenToReal[handle, zoomPt2.y, y]; graph.bounds _ [ xmin: MIN[x1, x2], xmax: MAX[x1, x2], ymin: MIN[y1, y2], ymax: MAX[y1, y2]]; PaintAll[handle]; }; }; -- ZoomWithZoomPts ClearZoomBox: PUBLIC GraphProc = { IF HandleNotNil[handle] THEN IF ViewerNotNil[handle.chart.viewer] THEN { OPEN handle; IF zoomPt2.set THEN { viewer: Viewer _ chart.viewer; screen: ViewerPrivate.Screen _ ViewerPrivate.ViewerScreen[viewer]; DrawZBox: PROC [context: Imager.Context] = {DrawZoomBox[context, handle]}; ViewerPrivate.PaintScreen[screen: screen, action: DrawZBox, suspendCarets: FALSE]; zoomPt2 _ []; } ELSE zoomPt1 _ []; }; }; -- ClearZoomBox }. LOG. SChen, create at October 9, 1985 6:16:32 pm PDT ˜GraphEdit.mesa, Copyright c 1985 by Xerox Corporation. All rights reserved. Last Edited by: Sweetsun Chen, October 22, 1985 3:17:16 pm PDT procs in this module may affect either the graph viewer or the controller, or both. invoked by ctrl-b or ctrl-d on chart, or menus on controller. invoked by T - X or T - Y on graph viewer; or menus on controller. Invoked by G - X or G - Y on graph viewer; or menus on controller. Invoked by plain red click or dragging red on graph viewer. (place is in real coordinates instead of screen coordinates.) Invoked by plain blue click on graph viewer. Invoked by 2-1 or 1-2 on graph viewer. entity Invoked by C-red on graph viewer. Invoked by C-blue on graph viewer. Invoked by Ctrl-C on graph viewer. Invoked by H-red on graph viewer. Invoked by H-blue on graph viewer. Move left horizontally toward the segment, (v1, v2), crossing the segment at (crossX, from.y). Move right horizontally toward the segment, (v1, v2), crossing the segment at (crossX, from.y). alreadOnIt: from is already on entity, and we prefer moving away than staying. not to include this segment into consideration. tx is included. not to skip to next point. tx is skiped. Invoked by V-red on graph viewer. Invoked by V-blue on graph viewer. Invoked by S-red on graph viewer. Invoked by n-red on graph viewer. Invoked by n-blue on graph viewer. Invoked by S-H-red on graph viewer. Go to a closest point on the selected curve to the left of cursor, where the y value is the same as cursorY. Special cases: If cursor is on a horizontal line segment, cursor stays where it is. If no other point on curve of the same y value as cursorY, cursor stays where it is. Invoked by S-H-blue on graph viewer. Go to a closest point on the selected curve to the left of cursor, where the y value is the same as cursorY. Special cases: If cursor is on a horizontal line segment, cursor stays where it is. If no other point on curve of the same y value as cursorY, cursor stays where it is. text Invoked by T-red on graph viewer. (sx, sy) is the position of cursor on screen. Invoked by T-blue on graph viewer. Invoked by T-yellow on graph viewer. (sx, sy) is the position of cursor on screen. Invoked by Ctrl-T on graph viewer. zooming Invoked by Z-red on graph viewer. Invoked by z-blue on graph viewer. Invoked by z-yellow or drag z-yellow on graph viewer. Invoked by z-(yellow up) while blue is not down, when zoomPt2 is set. Invoked by z-shift. Ê’˜JšœÏmœ0Ïc™Lšœ™Icode™.—J˜šÏk ˜ JšœŸœ˜JšœŸœsŸœ˜‚Jšœ Ÿœ6˜GJšœ Ÿœ ˜Jšœ ŸœÁ˜ÓJšœ ŸœÅ˜ÔJšœŸœFŸœ˜WJšœŸœ ˜JšœŸœ ˜-JšœŸœ˜"JšœŸœ˜JšœŸœ˜JšœŸœ%˜8Jšœ Ÿœ˜ —J˜šœ ŸœŸ˜JšŸœ˜•JšŸœ˜—J˜JšŸœ ˜$™SJ˜—šÏn œŸœŸœŸœ˜OJšœ=™=šŸœŸœŸœ˜+Jšœ%˜%šŸœŸœŸ˜šŸœ*Ÿ˜0JšœŸœŸœ Ÿœ˜N——šŸœŸœŸ˜,Jšœ,˜,—JšŸœŸœŸœ˜,J˜—Jšœž ˜J˜—š  œŸœŸœŸœ#žI˜¨šŸœŸœŸœ˜+JšŸœŸœŸœ/˜IšŸ˜JšŸœŸœ#˜@JšŸœ#˜'—šŸœŸœŸ˜šŸœ3Ÿœ˜:šœŸœŸ˜+Jšœ.Ÿœ˜E———J˜—Jšœž˜J˜—š  œŸœŸœŸœ#žQ˜®šŸœŸœŸœ˜+JšŸœŸœŸœC˜]šŸ˜JšŸœ!Ÿœ(˜OJšŸœ*˜.—šŸœŸœŸ˜šŸœ3Ÿœ˜:šœŸœŸ˜+Jšœ.Ÿœ˜E———J˜—Jšœž˜J˜—š   œŸœŸœŸœŸœ ˜DJšœB™BšŸœŸœŸœ˜+Jšœ+˜+šŸœŸœŸ˜šŸœ/Ÿ˜5JšœŸœŸœ Ÿœ ˜E——šŸœŸœŸœ˜.JšŸœŸœŸœ ˜2—J˜—Jšœž˜J˜—š   œŸœŸœŸœŸœ ˜BJšœB™BšŸœŸœŸœ˜+JšŸœŸœŸœ˜9Jšœ#˜#šŸœŸœŸœ)Ÿ˜HJšœŸœŸœŸœ ˜=—JšŸœŸœŸœ˜9J˜—Jšœž ˜J˜—š  œŸœ˜&Jšœy™yšŸœŸœŸœ#Ÿœ˜HJšŸœ˜ JšœŸœ+˜LšŸœŸœŸœ˜Jšœ˜Jšœ˜J˜—JšŸœŸœŸœ+Ÿœ˜KJšœ˜—Jšœž˜ J˜š  œŸœ7ŸœŸœŸœž ˜…JšŸœ˜ Jšœ-˜-šŸœŸœŸœ˜JšœŸœŸœ.Ÿœ˜eJšŸœŸœ˜,J˜—šŸœŸœŸœ˜4JšœQ˜Q—Jšœž˜J™——š  œŸœ˜"J™,š ŸœŸœŸœ#ŸœŸœ˜UJšœŸœ˜ šŸœŸœŸ˜JšœJŸœ˜Q—JšŸœŸœŸœ&˜GJ˜—Jšœž˜J˜—š  œŸœ˜#J™&šŸœŸœŸœ#Ÿ˜FJšœEŸœ˜K—Jšœž˜—J˜J™š  œŸœ˜'J™!šŸœŸœŸœ#Ÿœ˜HšœŸœŸœ Ÿœ˜JJšœŸœ˜Jšœ˜šœ+˜+Jšœ;˜;—J˜ šŸœŸœ ŸœŸœ˜3Jšœ#Ÿœ˜)J˜!J˜—JšœŸœ˜J˜ Jšœž ˜J˜—Jšœ0Ÿœ˜8šŸœ˜ JšŸœŸœŸœ"˜Ašœ"˜"JšŸœ8˜<—Jšœ˜—J˜—Jšœž˜J˜š  œŸœŸœŸœŸœŸœ˜‡Jšœ0˜0šŸœ ŸœŸœž>˜SJš ŸœŸœŸœŸœŸœ˜.šŸœ4Ÿœ ŸœŸ˜OJšŸœ ˜Jšœ Ÿœ˜+JšœŸœŸœ ˜šŸœ Ÿœ˜JšœŸœ˜JšœŸœ˜JšœŸœ˜JšœŸœ˜JšœŸœ˜JšœŸœ˜šŸœŸœŸœ˜?Jšœ ˜ Jšœ˜J˜—šŸœ˜JšœŸœ˜JšœŸœ˜JšœŸœ˜JšœŸœ˜ Jšœ Ÿœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜š ŸœŸœ ŸœŸœ Ÿœ˜BJšœ ˜ JšœŸœ Ÿœ Ÿœ ˜/J˜—J˜—J˜—J˜JšŸœ˜—J˜—Jšœž˜—J˜š œŸœ#ŸœŸœŸœŸœŸœ˜™JšŸœŸœŸœ˜-Jšœ Ÿœ˜#JšœŸœ˜ JšœŸœ˜šŸœ&ŸœŸœŸ˜:J˜ J˜JšŸœŸœŸœ˜8Jšœ7˜7šŸœŸœ˜Jšœ˜J˜Jšœ˜J˜—J˜JšŸœ˜—Jšœž˜—J™—š œŸœ˜$J™"JšŸœ#Ÿœ3˜\JšŸž˜J˜—š œŸœ˜*J™"š ŸœŸœŸœ#ŸœŸœ˜UJ˜&š Ÿœ ŸœŸœŸœŸœ˜+JšœM˜MJ˜#J˜—J˜—Jšœž˜J˜—š œŸœ˜&J™!š ŸœŸœŸœ#ŸœŸœ˜UJšœŸœ˜JšœŸœ˜J˜"Jšœ)˜)šŸœ,ŸœŸœŸ˜@JšœŸœ˜ Jšœ ŸœŸœ˜Jšœ4˜4š Ÿœ ŸœŸœŸœŸœŸœ˜HJšœ&˜&—JšŸœ˜—šŸœ ŸœŸœ˜Jšœ*Ÿœ˜0J˜J˜—Jšœ˜—Jšœž˜J˜—š œŸœ˜'J™"š ŸœŸœŸœ#ŸœŸœ˜UJšœŸœ˜JšœŸœ˜J˜"Jšœ(˜(šŸœ,ŸœŸœŸ˜@JšœŸœ˜ Jšœ ŸœŸœ˜Jšœ5˜5Jš Ÿœ ŸœŸœŸœŸœŸœ(˜nJšŸœ˜—šŸœ ŸœŸœ˜Jšœ*Ÿœ˜0J˜J˜—Jšœ˜—Jšœž˜J˜š œŸœŸœŸœ Ÿœ ŸœŸœ˜dJšœ^™^šŸœ&Ÿœ˜.šœ Ÿœ ŸœŸœ˜.JšŸœ4˜8—Jšœ˜J˜—Jšœž˜—J˜š œŸœŸœŸœ Ÿœ ŸœŸœ˜eJšœ_™_šŸœ&Ÿœ˜.šœ Ÿœ ŸœŸœ˜.JšŸœ4˜8—Jšœ˜J˜—Jšœž˜—J˜š œŸœŸœŸœŸœŸœŸœ ŸœŸœž˜œJšœN™NšŸœ ŸœŸœ˜Jšœ(Ÿœ˜,Jšœ1˜1Jšœ)˜)š Ÿœ ŸœŸœ ŸœŸ˜#JšœŸœ˜š ŸœŸœŸœŸœŸœ˜'Jšœ/™/—JšœŸœ ˜JšœŸœ˜!š ŸœŸœŸœŸœŸœŸœ˜+J™—Jšœ˜Jšœ˜JšŸœ˜—Jšœ˜Jšœ˜š Ÿœ ŸœŸœŸœž3˜Sš Ÿœ ŸœŸœ ŸœŸ˜)šœ*˜*Jšœ:˜:——Jšœ˜J˜JšŸœ˜—Jšœ*˜*Jšœ*˜*J˜—Jšœž˜J˜—š œŸœŸœŸœŸœŸœŸœ ŸœŸœ˜~šŸœ ŸœŸœ˜Jšœ1˜1Jšœ)˜)š Ÿœ ŸœŸœ ŸœŸ˜#JšœŸœ˜š ŸœŸœŸœŸœŸœŸœ˜+J™—Jšœ˜Jšœ˜š ŸœŸœŸœŸœŸœ˜'J™ —JšŸœ˜—š Ÿœ Ÿœ ŸœŸœ ŸœŸ˜0š ŸœŸœŸœŸœŸ˜-šœ+˜+JšœR˜R——Jšœ˜J˜JšŸœ˜—J˜—Jšœž˜—J˜—š œŸœ˜$J™!š ŸœŸœŸœ#ŸœŸœ˜UJšœ Ÿœ˜.Jšœ Ÿœ ˜.JšœŸœ˜šŸœ,ŸœŸœŸ˜@JšœŸœ˜ JšœŸœ˜JšœR˜RšŸœŸœ˜ š œŸœŸœ ŸœŸœ Ÿœ ˜:JšŸœ4˜8—š Ÿœ Ÿœ ŸœŸœŸœŸœ˜BJ˜J˜ J˜—J˜—JšŸœ˜—šŸœ ŸœŸœ˜Jšœ$Ÿœ˜*J˜J˜—J˜—Jšœž˜J˜—š œŸœ˜&J™"š ŸœŸœŸœ#ŸœŸœ˜UJšœ Ÿœ˜.Jšœ Ÿœ!˜/JšœŸœ˜šŸœ,ŸœŸœŸ˜@JšœŸœ˜ JšœŸœ˜JšœR˜RšŸœŸœ˜ š œŸœŸœ ŸœŸœ Ÿœ ˜:JšŸœ4˜8—š Ÿœ Ÿœ ŸœŸœŸœŸœ˜BJ˜J˜ J˜—J˜—JšŸœ˜—šŸœ ŸœŸœ˜Jšœ$Ÿœ˜*J˜J˜—J˜—Jšœž˜J˜—š œŸœ˜+J™!š ŸœŸœŸœ#ŸœŸœ˜UšŸœŸœŸœ˜$JšœŸœ˜šœ!˜!JšœT˜T—Jšœ'Ÿœ˜-J˜—J˜—Jšœž˜J™—š  œŸœŸœŸœ Ÿœ ˜LJ™!š ŸœŸœŸœ#ŸœŸœ˜UšŸœŸœŸœ˜$Jšœ Ÿœ˜-Jšœ>˜>Jšœ6˜6Jšœ"Ÿœ˜&JšœŸœ˜ šŸœŸœŸœŸ˜.JšŸœŸœŸœ˜%Jšœ Ÿœ˜,Jšœ Ÿœ˜,Jšœ˜JšŸœ˜—šŸœ Ÿ˜šŸœ ŸœŸœ˜šœ˜J˜7—JšŸœ˜J˜—Jšœ)˜)šŸœŸœŸœ˜3JšœKŸœ˜Q—J˜ Jšœ˜Jšœ˜JšŸœ˜ —JšœŸœ˜/šŸœŸœŸœ˜Jšœ˜Jšœ˜J˜—J˜—J˜—Jšœž˜J˜—š  œŸœŸœŸœ Ÿœ ˜MJ™#š ŸœŸœŸœ#ŸœŸœ˜UšŸœŸœŸœ˜$Jšœ Ÿœ˜-Jšœ>˜>Jšœ6˜6JšœŸœ˜ šŸœŸœŸœŸ˜.JšŸœŸœŸœ˜$JšŸœ˜—šŸœŸœ Ÿ˜/šŸœŸœŸœ˜šœ˜J˜7—JšŸœ˜J˜—Jšœ'˜'šŸœŸœŸœ˜3JšœKŸœ˜Q—J˜ Jšœ˜JšŸœ˜ —JšœŸœ˜/šŸœŸœŸœ˜Jšœ˜Jšœ˜J˜—J˜—J˜—Jšœž˜J˜—š œŸœ˜'J™™J™DJ™T—š ŸœŸœŸœ#ŸœŸœ˜UJšœŸœ˜ JšœŸœ ˜*JšœŸœ ˜*JšœŸœ˜ JšœFŸœ˜Lš ŸœŸœŸœŸœŸ˜,Jšœ.Ÿœ˜4—J˜—Jšœž˜J˜—š œŸœ˜(J™‘™J™DJ™T—š ŸœŸœŸœ#ŸœŸœ˜UJšœŸœ˜ JšœŸœ ˜*JšœŸœ ˜*JšœŸœ˜ JšœG˜Gš ŸœŸœŸœŸœŸ˜,Jšœ.Ÿœ˜4—J˜—Jšœž˜—J˜J™š  œŸœ˜%J™!J™.š ŸœŸœŸœ#ŸœŸœ˜UšŸœŸœ˜"JšœŸœ˜Jšœ Ÿœž ˜šŸœ ŸœŸœŸ˜3Jšœ˜JšœQ˜QšŸœ-Ÿœ˜3Jšœ,ŸœŸœ˜I—JšŸœ˜—šŸœ ŸœŸœ˜šœ˜Jšœ@Ÿœ˜F—Jšœ˜J˜—J˜—J˜—Jšœž ˜J˜—š  œŸœ˜"J™"JšŸœ#Ÿœ/˜XJšœž˜J˜—š  œŸœ˜&J™$Jšœ-™-š ŸœŸœŸœ#ŸœŸœ˜UJ˜ šŸœŸœŸœ˜Jšœ?Ÿœ˜EJšœ˜JšœK˜KJšœ˜J˜—J˜—Jšœž˜J˜—š œŸœ˜(J™"šŸœŸœŸœ˜+J˜ š ŸœŸœŸœŸœŸœ˜)JšœI˜IJšœ˜J˜—J˜—Jšœž˜—J˜J™JšœŸœ ˜"JšœŸœ ˜#š œŸœ˜!J™!š ŸœŸœŸœ#ŸœŸœ˜UJšœŸœ$˜6JšœŸœ˜)JšœŸœ˜)šœ?˜?Jšœ0˜0—JšœŸœ˜J˜J˜—Jšœž ˜ J™—š œŸœ˜"J™"š ŸœŸœŸœ#ŸœŸœ˜UJšœŸœ$˜6JšœŸœ˜*JšœŸœ˜*šœ?˜?Jšœ0˜0—JšœŸœ˜Jšœ˜J˜—Jšœž ˜ J™—š  œŸœ˜$J™5š ŸœŸœŸœ#ŸœŸœ˜UšŸœ Ÿœ˜Jšœ˜J˜BJš œŸœ<˜JKšŸœ ŸœLŸœ˜fKšœ Ÿœ ˜KšœKŸœ˜RJ˜—KšŸœ Ÿœ ˜K˜—Kšœž ˜K˜š  œŸœ4Ÿœ˜Q˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜J˜—Jšœ˜Kšœ(˜(KšœŸœ˜Kšœž˜—K˜—š œŸœ˜%KšœE™Eš ŸœŸœŸœ#ŸœŸœ˜UJšœŸœ˜Jšœ(˜(Jšœ(˜(Jšœ(˜(Jšœ(˜(˜Jš œŸœŸœŸœŸœ ˜L—J˜J˜—Jšœž˜K™—š  œŸœ˜"Kšœ™š ŸœŸœŸœ#ŸœŸœ˜UšŸœ Ÿœ˜Jšœ˜J˜BJš œŸœ<˜JKšœKŸœ˜RKšœ ˜ J˜—KšŸœ˜K˜—Kšœž˜—J˜J˜šŸœ˜J˜/——…—S¶wà