DIRECTORY Atom, Commander, Convert, SampledCurveEdit, InputFocus, FS, Graphics, IO, Menus, MessageWindow, Process, Rope, RuntimeError, TIPUser, ViewerClasses, ViewerOps ; SampledCurveEditImpl: CEDAR MONITOR IMPORTS Atom, Commander, Convert, InputFocus, FS, Graphics, IO, Menus, MessageWindow, Process, Rope, RuntimeError, TIPUser, ViewerOps EXPORTS SampledCurveEdit ~ BEGIN OPEN SampledCurveEdit; header: MarkedPoint ~ [2, 2, TRUE, open]; ROPE: TYPE ~ Rope.ROPE; Tool: TYPE = REF ToolRec; ToolRec: TYPE = RECORD [ actionQueue: LIST OF REF, actionQueueEmpty: CONDITION, outline: LIST OF Trajectory, grabbed: LIST OF MarkedPoint, grabbedCount: NAT, selected: LIST OF MarkedPoint _ NIL, selectedCount: NAT _ 0, dragSource: MarkedPoint _ header, paintRectangles: LIST OF PaintRectangle ]; PaintRectangle: TYPE ~ RECORD [ xMin, yMin, xMax, yMax: REAL ]; InitViewer: PROC [self: Viewer] = { tool: Tool _ NEW[ToolRec]; tool.outline _ LIST[LIST[header]]; tool.grabbed _ NIL; tool.grabbedCount _ 0; self.column _ left; self.data _ tool; IF self.file # NIL THEN { LoadFile[self]; }; }; LoadFile: PROC [self: Viewer] = { Err: PROC [msg: ROPE, pos: INT _ -1] ~ { MessageWindow.Append[msg, TRUE]; IF pos>=0 THEN MessageWindow.Append[Convert.RopeFromInt[pos], FALSE]; MessageWindow.Blink[]; }; stream: IO.STREAM _ FS.StreamOpen[self.file, $read ! FS.Error => {MessageWindow.Append[error.explanation, TRUE]; MessageWindow.Blink[]; GOTO Quit}]; outline: Outline _ NIL; stack: ARRAY [0..10) OF REAL; stackTop: INT _ 0; PushReal: PROC [real: REAL] ~ { IF stackTop >= 10 THEN {Err["SampledCurveEdit stack overflow at ", stream.GetIndex]; RETURN}; stack[stackTop] _ real; stackTop _ stackTop + 1; }; PopReal: PROC RETURNS [real: REAL] ~ { IF stackTop <= 0 THEN {Err["SampledCurveEdit stack underflow at ", stream.GetIndex]; RETURN [0]}; stackTop _ stackTop - 1; real _ stack[stackTop]; }; curTrajEnd: Trajectory _ NIL; DO r: REF ANY _ NIL; r _ stream.GetRefAny[ ! IO.EndOfStream => CONTINUE]; IF r = NIL THEN EXIT; WITH r SELECT FROM refInt: REF INT => PushReal[refInt^]; refCard: REF LONG CARDINAL => PushReal[refCard^]; refReal: REF REAL => PushReal[refReal^]; atom: ATOM => { SELECT atom FROM $MOVETO => { y: REAL _ PopReal[]; x: REAL _ PopReal[]; outline _ CONS[LIST[[x: 0, y: 0, isHeader: TRUE, kind: open], [x: x, y: y, isHeader: FALSE, kind: sample]], outline]; curTrajEnd _ outline.first.rest; }; $LINETO => { y: REAL _ PopReal[]; x: REAL _ PopReal[]; IF curTrajEnd = NIL THEN {Err["SampledCurveEdit Missing MOVETO", stream.GetIndex]; EXIT}; curTrajEnd.rest _ LIST[[x: x, y: y, isHeader: FALSE, kind: sample]]; curTrajEnd _ curTrajEnd.rest; }; $CLOSE => { y: REAL _ curTrajEnd.first.y; x: REAL _ curTrajEnd.first.x; outline.first.first.x _ x; outline.first.first.y _ y; outline.first.first.kind _ sample; curTrajEnd _ NIL; }; ENDCASE => {Err["SampledCurveEdit parse error at position ", stream.GetIndex]; EXIT}; }; ENDCASE => {Err["SampledCurveEdit parse error at position ", stream.GetIndex]; EXIT}; ENDLOOP; IF stackTop # 0 THEN Err["SampledCurveEdit stack not empty at exit"]; SetOutline[self, outline]; stream.Close; EXITS Quit => NULL; }; SaveProc: PROC [self: Viewer, force: BOOL _ FALSE] = { IF self.file # NIL THEN { stream: IO.STREAM _ FS.StreamOpen[self.file, $create ! FS.Error => {MessageWindow.Append[error.explanation, TRUE]; MessageWindow.Blink[]; GOTO Quit}]; outline: Outline _ NIL; WITH self.data SELECT FROM tool: Tool => {outline _ tool.outline}; ENDCASE => NULL; WHILE outline # NIL DO traj: Trajectory _ outline.first; IF traj.rest # NIL THEN { stream.PutF["%g %g MOVETO\n", IO.real[traj.rest.first.x], IO.real[traj.rest.first.y]]; FOR p: PointList _ traj.rest.rest, p.rest UNTIL p=NIL DO stream.PutF[" %g %g LINETO\n", IO.real[p.first.x], IO.real[p.first.y]]; ENDLOOP; IF traj.rest.first.kind # open THEN { stream.PutRope[" CLOSE"]; }; }; outline _ outline.rest; ENDLOOP; stream.Close; }; EXITS Quit => NULL; }; PaintPoint: PROCEDURE [context: Graphics.Context, x, y: REAL] = { Graphics.SetColor[context, Graphics.black]; Graphics.DrawBox[context, [x-2, y-2, x+2, y+2]]; Graphics.SetColor[context, Graphics.white]; Graphics.DrawBox[context, [x-1, y-1, x+1, y+1]]; }; DrawOutline: PROCEDURE [tool: Tool, context: Graphics.Context] = { FOR t: LIST OF Trajectory _ tool.outline, t.rest UNTIL t=NIL DO IF t.first.rest # NIL THEN { start: MarkedPoint _ t.first.first; IF start.kind = open THEN start _ t.first.rest.first; Graphics.SetCP[context, start.x, start.y]; FOR p: LIST OF MarkedPoint _ t.first.rest.rest, p.rest UNTIL p=NIL DO Graphics.DrawTo[context, p.first.x, p.first.y]; ENDLOOP; }; ENDLOOP; IF tool.outline # NIL THEN { FOR p: LIST OF MarkedPoint _ tool.outline.first.rest, p.rest UNTIL p=NIL DO PaintPoint[context, p.first.x, p.first.y]; ENDLOOP; }; }; InvertSelection: PROCEDURE [viewer: Viewer, context: Graphics.Context] = { tool: Tool _ NARROW[viewer.data]; [] _ Graphics.SetPaintMode[context, invert]; Graphics.SetColor[context, Graphics.black]; IF tool.selected # NIL THEN { list: LIST OF MarkedPoint _ tool.selected.rest; FOR i: NAT IN [0..tool.selectedCount) DO p: MarkedPoint _ list.first; Graphics.DrawBox[context, [p.x-4, p.y-4, p.x+4, p.y+4]]; list _ list.rest; IF list = NIL THEN list _ tool.outline.first.rest; ENDLOOP; }; }; MagSqr: PROCEDURE [x, y: REAL] RETURNS [REAL] = {RETURN[x*x+y*y]}; SelectNewTrajectory: PROCEDURE [tool: Tool, v: MarkedPoint] RETURNS [changed: BOOLEAN] ~ { d: REAL _ 99999999; last: LIST OF Trajectory _ NIL; new: LIST OF Trajectory _ NIL; IF tool.outline = NIL THEN RETURN [FALSE]; FOR t: LIST OF Trajectory _ tool.outline, t.rest UNTIL t=NIL DO FOR p: LIST OF MarkedPoint _ t.first, p.rest UNTIL p.rest=NIL DO markedPoint: MarkedPoint _ p.rest.first; s: REAL _ MagSqr[markedPoint.x-v.x, markedPoint.y-v.y]; IF s < d THEN {new _ t; d _ s}; ENDLOOP; last _ t; ENDLOOP; IF new = tool.outline THEN RETURN [FALSE]; last.rest _ tool.outline; DO IF tool.outline.rest = new THEN {tool.outline.rest _ NIL; tool.outline _ new; RETURN [TRUE]}; tool.outline _ tool.outline.rest; ENDLOOP; }; GrabPoint: PROCEDURE [tool: Tool, v: MarkedPoint] = { d: REAL _ 99999999; tool.grabbed _ NIL; tool.grabbedCount _ 0; IF tool.outline # NIL THEN { FOR p: LIST OF MarkedPoint _ tool.outline.first, p.rest UNTIL p.rest=NIL DO markedPoint: MarkedPoint _ p.rest.first; s: REAL _ MagSqr[markedPoint.x-v.x, markedPoint.y-v.y]; IF s < d THEN {tool.grabbed _ p; tool.grabbedCount _ 1; d _ s}; ENDLOOP; }; }; metersPerPixel: REAL ~ 3.527777e-4; pixelsPerMeter: REAL ~ 1.0/metersPerPixel; PaintProc: ViewerClasses.PaintProc = { tool: Tool _ NARROW[self.data]; SELECT whatChanged FROM NIL => Notify[self, LIST[$Refresh]]; $PaintAll => { Graphics.SetColor[context, Graphics.white]; Graphics.DrawBox[context, [0, 0, self.cw, self.ch]]; Graphics.SetColor[context, Graphics.black]; DrawOutline[tool, context]; InvertSelection[self, context]; }; $TouchUp => { Graphics.SetColor[context, Graphics.white]; FOR paintList: LIST OF PaintRectangle _ tool.paintRectangles, paintList.rest UNTIL paintList = NIL DO box: PaintRectangle ~ paintList.first; Graphics.DrawBox[context, [box.xMin, box.yMin, box.xMax, box.yMax]]; ENDLOOP; Graphics.SetColor[context, Graphics.black]; FOR paintList: LIST OF PaintRectangle _ tool.paintRectangles, paintList.rest UNTIL paintList = NIL DO box: PaintRectangle ~ paintList.first; mark: Graphics.Mark ~ Graphics.Save[context]; Graphics.ClipBox[context, [box.xMin, box.yMin, box.xMax, box.yMax]]; DrawOutline[tool, context]; InvertSelection[self, context]; Graphics.Restore[context, mark]; ENDLOOP; }; $NewPt => { p: MarkedPoint _ tool.outline.first.rest.first; Graphics.SetColor[context, Graphics.black]; IF tool.outline.first.rest.rest # NIL THEN { q: MarkedPoint _ tool.outline.first.rest.rest.first; Graphics.SetCP[context, q.x, q.y]; Graphics.DrawTo[context, p.x, p.y]; PaintPoint[context, q.x, q.y]; }; PaintPoint[context, p.x, p.y]; }; $InvertGrabbed => IF tool.grabbed # NIL THEN { prevKind: PointKind _ open; list: LIST OF MarkedPoint; [] _ Graphics.SetPaintMode[context, invert]; list _ tool.grabbed.rest; FOR i: NAT IN [0..tool.grabbedCount) WHILE list # NIL DO p: MarkedPoint _ list.first; Graphics.DrawBox[context, [p.x-2, p.y-2, p.x+2, p.y+2]]; list _ list.rest; ENDLOOP; Graphics.SetCP[context, tool.grabbed.first.x, tool.grabbed.first.y]; prevKind _ tool.grabbed.first.kind; list _ tool.grabbed.rest; FOR i: NAT IN [0..tool.grabbedCount+1) WHILE list # NIL DO p: MarkedPoint _ list.first; IF prevKind = open OR p.kind = open THEN Graphics.SetCP[context, p.x, p.y] ELSE Graphics.DrawTo[context, p.x, p.y]; prevKind _ p.kind; list _ list.rest; ENDLOOP; }; $EraseGrabbedPoint => IF tool.grabbed # NIL THEN { list: LIST OF MarkedPoint _ tool.grabbed.rest; Graphics.SetColor[context, Graphics.white]; FOR i: NAT IN [0..tool.grabbedCount) WHILE list # NIL DO p: MarkedPoint _ list.first; Graphics.DrawBox[context, [p.x-2, p.y-2, p.x+2, p.y+2]]; list _ list.rest; ENDLOOP; }; $InvertSel => InvertSelection[self, context]; ENDCASE => ERROR; }; ActionWithPoint: TYPE = REF ActionWithPointRep; ActionWithPointRep: TYPE = RECORD [ atom: ATOM, markedPoint: MarkedPoint ]; MalformedTrajectory: PUBLIC ERROR ~ CODE; CopyTrajectory: PUBLIC PROC [trajectory: Trajectory] RETURNS [Trajectory] ~ { IF trajectory = NIL THEN ERROR MalformedTrajectory ELSE { new: Trajectory ~ LIST[trajectory.first]; end: Trajectory _ new; markedPoint: MarkedPoint _ trajectory.first; IF NOT new.first.isHeader THEN ERROR MalformedTrajectory; WHILE (trajectory _ trajectory.rest) # NIL DO markedPoint _ trajectory.first; IF markedPoint.isHeader THEN ERROR MalformedTrajectory; end.rest _ LIST[markedPoint]; end _ end.rest; ENDLOOP; IF new.first.kind # open THEN { headPoint: MarkedPoint _ new.first; headPoint.isHeader _ markedPoint.isHeader; IF headPoint # markedPoint THEN ERROR MalformedTrajectory; }; RETURN [new]; }; }; CopyOutline: PUBLIC PROC [outline: Outline] RETURNS [Outline] ~ { new: Outline ~ LIST[NIL]; end: Outline _ new; WHILE outline # NIL DO traj: Trajectory _ CopyTrajectory[outline.first]; end.rest _ LIST[traj]; end _ end.rest; outline _ outline.rest; ENDLOOP; RETURN [new.rest]; }; GetOutline: PUBLIC ENTRY PROC [viewer: Viewer] RETURNS [Outline] ~ { ENABLE UNWIND => NULL; WITH viewer.data SELECT FROM tool: Tool => { UNTIL tool.actionQueue = NIL DO WAIT tool.actionQueueEmpty ENDLOOP; RETURN [CopyOutline[tool.outline]] }; ENDCASE => RETURN [NIL]; }; ObtainOutline: PUBLIC ENTRY PROC [viewer: Viewer] RETURNS [outline: Outline] ~ { ENABLE UNWIND => NULL; WITH viewer.data SELECT FROM tool: Tool => { UNTIL tool.actionQueue = NIL DO WAIT tool.actionQueueEmpty ENDLOOP; outline _ tool.outline; tool.outline _ NIL; tool.grabbed _ tool.selected _ NIL; tool.grabbedCount _ tool.selectedCount _ 0; tool.actionQueue _ LIST[NIL, $PaintAll]; TRUSTED {Process.Detach[FORK DispatchProcess[viewer]]}; }; ENDCASE => RETURN [NIL]; }; SetOutline: PUBLIC PROC [viewer: Viewer, outline: Outline] ~ { viewer.class.notify[viewer, LIST[outline]]; }; Notify: ENTRY ViewerClasses.NotifyProc = { ENABLE {UNWIND => NULL; RuntimeError.UNCAUGHT => {MessageWindow.Append["SampledCurveEdit UNCAUGHT ERROR in Notify", TRUE]; MessageWindow.Blink[]; GOTO Quit}}; tool: Tool _ NARROW[self.data]; viewer: Viewer ~ self; Unknown: PROC ~ { stream: IO.STREAM _ IO.ROS[]; stream.PutRope["Unknown input: "]; stream.Put[IO.refAny[input]]; MessageWindow.Append[IO.RopeFromROS[stream], TRUE]; stream.Close[]; }; ActionKind: TYPE ~ {Ordinary, AutoInverse, Idempotent}; Queue: PROC [action: REF, kind: ActionKind _ $Ordinary, markedPoint: MarkedPoint _ header] ~ { LastAction: PROC RETURNS [REF] ~ { RETURN [WITH ultimate.first SELECT FROM actionWithPoint: ActionWithPoint => actionWithPoint.atom, ENDCASE => ultimate.first ] }; penultimate: LIST OF REF _ NIL; ultimate: LIST OF REF _ tool.actionQueue; IF ultimate = NIL THEN TRUSTED { ultimate _ tool.actionQueue _ LIST[NIL]; Process.Detach[FORK DispatchProcess[viewer]]; }; FOR q: LIST OF REF _ tool.actionQueue, q.rest UNTIL q = NIL DO penultimate _ ultimate; ultimate _ q; ENDLOOP; SELECT kind FROM $Ordinary => NULL; $AutoInverse => IF LastAction[] = action THEN {penultimate.rest _ NIL; RETURN}; $Idempotent => IF LastAction[] = action THEN { WITH ultimate.first SELECT FROM actionWithPoint: ActionWithPoint => actionWithPoint.markedPoint _ markedPoint; ENDCASE => NULL; RETURN }; ENDCASE => ERROR; ultimate.rest _ LIST[ IF markedPoint = header THEN action ELSE NEW[ActionWithPointRep _ [NARROW[action], markedPoint]] ]; }; QueuePaintAll: PROC ~ { q: LIST OF REF _ tool.actionQueue; IF q # NIL THEN { WHILE q.rest#NIL DO SELECT q.rest.first FROM $InvertSel, $InvertGrabbed, $TouchUp, $EraseGrabbedPoint, $PaintAll => q.rest _ q.rest.rest; ENDCASE => q _ q.rest; ENDLOOP; }; Queue[$PaintAll]; }; QueueTouchUp: PROC ~ { q: LIST OF REF _ tool.actionQueue; deferred: LIST OF REF _ NIL; IF q # NIL THEN { WHILE q.rest#NIL DO SELECT q.rest.first FROM $TouchUp => { deferred _ q.rest; q.rest _ q.rest.rest; deferred.rest _ NIL; }; $InvertSel, $InvertGrabbed, $EraseGrabbedPoint => { IF deferred # NIL THEN { deferred.rest _ q.rest; q.rest _ deferred; deferred _ NIL; q _ q.rest; }; q _ q.rest; }; ENDCASE => q _ q.rest; ENDLOOP; IF deferred # NIL THEN {q.rest _ deferred; RETURN}; }; Queue[$TouchUp]; }; WITH input.first SELECT FROM atom: ATOM => { SELECT atom FROM $Clear => {Queue[$Clear]; QueuePaintAll[]}; $Delete => { Queue[$DeleteSelected]; QueueTouchUp[]; }; $Refresh => QueuePaintAll[]; $Reverse => { Queue[$InvertSel, $AutoInverse]; Queue[$ClearSel, $Idempotent]; Queue[$ReverseTrajectory, $AutoInverse]; }; $Save => { Queue[$Save, $Idempotent]; }; $Smooth => { Queue[$Smooth]; QueueTouchUp[]; }; ENDCASE => { WITH Atom.GetProp[atom, viewerClass] SELECT FROM pointModifier: PointModifier => { Queue[pointModifier]; QueueTouchUp[]; }; ENDCASE => Unknown[]; }; }; outline: Outline => { Queue[$ClearSel, $Idempotent]; Queue[outline]; QueuePaintAll[]; }; mousePlace: TIPUser.TIPScreenCoords => { markedPoint: MarkedPoint _ [mousePlace.mouseX, mousePlace.mouseY, FALSE, sample]; IF InputFocus.GetInputFocus[].owner # viewer THEN InputFocus.SetInputFocus[viewer]; SELECT input.rest.first FROM $AddPt => { Queue[$InvertSel, $AutoInverse]; Queue[$AddPt, $Ordinary, markedPoint]; Queue[$NewPt]; Queue[$InvertSel, $AutoInverse]; }; $DeletePt => { Queue[$GrabPt]; Queue[$DeleteGrabbed]; QueueTouchUp[]; }; $ExtendSelection => { Queue[$InvertSel, $AutoInverse]; Queue[$ExtendSel, $Idempotent, markedPoint]; Queue[$InvertSel, $AutoInverse]; }; $GrabPt => { Queue[$InvertSel, $AutoInverse]; Queue[$GrabPt, $Ordinary, markedPoint]; Queue[$InvertGrabbed]; Queue[$EraseGrabbedPoint]; Queue[$RecordGrabbedBox]; Queue[$MoveTo, $Idempotent, markedPoint]; Queue[$InvertGrabbed, $AutoInverse]; Queue[$InvertSel, $AutoInverse]; }; $MovePt => { Queue[$InvertSel, $AutoInverse]; Queue[$InvertGrabbed, $AutoInverse]; Queue[$MoveTo, $Idempotent, markedPoint]; Queue[$InvertGrabbed, $AutoInverse]; Queue[$InvertSel, $AutoInverse]; }; $ReleasePt => { Queue[$RecordGrabbedBox]; Queue[$ClearGrabbed]; QueueTouchUp[]; }; $SelectPt => { Queue[$InvertSel, $AutoInverse]; Queue[$SelectPt, $Idempotent, markedPoint]; Queue[$InvertSel, $AutoInverse]; }; $SelectTrajectory => { Queue[$RecordSelectionBox]; Queue[$ClearSel, $Idempotent]; Queue[$SelectTrajectory, $Idempotent, markedPoint]; QueueTouchUp[]; }; $ShowPt => { s: IO.STREAM _ IO.ROS[]; s.PutF["%7.1g %7.1g ", IO.real[markedPoint.x], IO.real[markedPoint.y]]; MessageWindow.Append[s.RopeFromROS, TRUE]; }; $StartPt => { Queue[$InvertSel, $AutoInverse]; Queue[$ClearSel, $Idempotent]; Queue[$RecordTrajectoryBox]; Queue[$StartPt, $Ordinary, markedPoint]; QueueTouchUp[]; Queue[$NewPt]; }; ENDCASE => Unknown[]; }; ENDCASE => Unknown[]; EXITS Quit => NULL; }; Dequeue: ENTRY PROC [tool: Tool] RETURNS [ref: REF] ~ { ENABLE UNWIND => NULL; IF tool.actionQueue.rest = NIL THEN { tool.actionQueue _ NIL; ref _ NIL; NOTIFY tool.actionQueueEmpty; } ELSE { ref _ tool.actionQueue.rest.first; tool.actionQueue.rest _ tool.actionQueue.rest.rest; }; }; RegisterPointModifer: PUBLIC PROC [atom: ATOM, pointModifier: PointModifier] ~ { Atom.PutProp[atom, viewerClass, pointModifier]; }; DispatchProcess: PROC [viewer: Viewer] ~ { tool: Tool _ NARROW[viewer.data]; ref: REF; box: PaintRectangle _ [xMin: 999999, yMin: 999999, xMax: -999999, yMax: -999999]; ResetBBox: PROC ~ {box _ [xMin: 999999, yMin: 999999, xMax: -999999, yMax: -999999]}; slop: REAL ~ 5; BBPoint: PROC [markedPoint: MarkedPoint] ~ { box.xMin _ MIN[box.xMin, markedPoint.x-slop]; box.xMax _ MAX[box.xMax, markedPoint.x+slop]; box.yMin _ MIN[box.yMin, markedPoint.y-slop]; box.yMax _ MAX[box.yMax, markedPoint.y+slop]; }; RecordBBox: PROC ~ { IF box.xMin>box.xMax THEN RETURN; FOR paintList: LIST OF PaintRectangle _ tool.paintRectangles, paintList.rest UNTIL paintList = NIL DO old: PaintRectangle ~ paintList.first; IF old.xMin<=box.xMin AND old.yMin<=box.yMin AND old.xMax>=box.xMax AND old.yMax>=box.yMax THEN { ResetBBox[]; RETURN; }; IF box.xMin<=old.xMin AND box.yMin<=old.yMin AND box.xMax>=old.xMax AND box.yMax>=old.yMax THEN { paintList.first _ box; ResetBBox[]; RETURN; }; ENDLOOP; tool.paintRectangles _ CONS[box, tool.paintRectangles]; }; BoundPoints: PROC [list: LIST OF MarkedPoint, count: NAT] ~ { FOR i: NAT IN [0..count) WHILE list # NIL DO IF list.first.kind # open THEN BBPoint[list.first]; list _ list.rest; ENDLOOP; }; RecordPoints: PROC [list: LIST OF MarkedPoint, count: NAT] ~ { BoundPoints[list, count]; RecordBBox[]; }; WHILE (ref _ Dequeue[tool]) # NIL DO action: ATOM _ $NothingMoreToDo; markedPoint: MarkedPoint; WITH ref SELECT FROM atom: ATOM => action _ atom; actionWithPoint: ActionWithPoint => { action _ actionWithPoint.atom; markedPoint _ actionWithPoint.markedPoint; }; pointModifier: PointModifier => { IF tool.selected # NIL THEN { BoundPoints[tool.selected, tool.selectedCount+2]; IF pointModifier.pointModifyProc[pointModifier, tool.selected, tool.selectedCount] THEN RecordPoints[tool.selected, tool.selectedCount+2] ELSE ResetBBox[]; } ELSE [] _ pointModifier.pointModifyProc[pointModifier, NIL, 0]; }; outline: Outline => { tool.outline _ outline; }; ENDCASE => ERROR; SELECT action FROM $NothingMoreToDo => NULL; $Save => { ViewerOps.SaveViewer[viewer]; }; $StartPt => { tool.selected _ NIL; tool.selectedCount _ 0; tool.grabbed _ NIL; tool.grabbedCount _ 0; tool.outline _ CONS[LIST[header, markedPoint], tool.outline]; ViewerOps.SetNewVersion[viewer]; }; $SelectTrajectory => { BoundPoints[tool.outline.first, LAST[NAT]]; IF SelectNewTrajectory[tool, markedPoint] THEN RecordPoints[tool.outline.first, LAST[NAT]] ELSE ResetBBox[]; }; $AddPt => { IF tool.outline.first.rest # NIL AND tool.outline.rest.rest # NIL THEN { tool.outline.first.rest.first.kind _ sample; }; tool.outline.first.rest _ CONS[markedPoint, tool.outline.first.rest]; ViewerOps.SetNewVersion[viewer]; }; $Clear => { tool.outline.first.rest _ tool.grabbed _ tool.selected _ NIL; tool.outline.rest _ NIL; tool.grabbedCount _ tool.selectedCount _ 0; ViewerOps.SetNewVersion[viewer]; }; $ClearGrabbed => { tool.grabbed _ NIL; tool.grabbedCount _ 0; }; $ClearSel => { tool.selected _ NIL; tool.selectedCount _ 0; tool.grabbed _ NIL; tool.grabbedCount _ 0; }; $DeleteGrabbed => { IF tool.outline.first.rest # NIL AND tool.grabbed # NIL THEN { BBPoint[tool.grabbed.first]; BBPoint[tool.grabbed.rest.first]; IF tool.grabbed.rest.rest # NIL THEN BBPoint[tool.grabbed.rest.rest.first]; RecordBBox[]; markedPoint _ tool.grabbed.rest.first; tool.grabbed.rest _ tool.grabbed.rest.rest; }; tool.grabbed _ tool.selected _ NIL; tool.grabbedCount _ tool.selectedCount _ 0; ViewerOps.SetNewVersion[viewer]; }; $DeleteSelected => { IF tool.selected # NIL THEN { RecordPoints[tool.selected, tool.selectedCount+2]; THROUGH [0..tool.selectedCount) WHILE tool.selected.rest # NIL DO tool.selected.rest _ tool.selected.rest.rest; ENDLOOP; }; tool.selected _ NIL; tool.selectedCount _ 0; tool.grabbed _ NIL; tool.grabbedCount _ 0; ViewerOps.SetNewVersion[viewer]; }; $ExtendSel => IF tool.selected # NIL THEN { new: LIST OF MarkedPoint _ NIL; count: INT _ 0; grabbedToGo: INT _ LAST[INT]; selectedToGo: INT _ LAST[INT]; GrabPoint[tool, markedPoint]; FOR list: LIST OF MarkedPoint _ tool.outline.first, list.rest UNTIL list = NIL OR (grabbedToGo<=0 AND selectedToGo<=0) DO IF list = tool.grabbed THEN { IF new = NIL THEN new _ tool.grabbed; grabbedToGo _ tool.grabbedCount; }; IF list = tool.selected THEN { IF new = NIL THEN new _ tool.selected; selectedToGo _ tool.selectedCount; }; IF new # NIL THEN count _ count + 1; grabbedToGo _ grabbedToGo - 1; selectedToGo _ selectedToGo - 1; ENDLOOP; tool.selected _ new; tool.selectedCount _ count; tool.grabbed _ NIL; tool.grabbedCount _ 0; ViewerOps.SetNewVersion[viewer]; }; $GrabPt => GrabPoint[tool, markedPoint]; $MoveTo => { markedPoint.kind _ tool.grabbed.rest.first.kind; tool.grabbed.rest.first _ markedPoint; ViewerOps.SetNewVersion[viewer]; }; $RecordGrabbedBox => RecordPoints[tool.grabbed, tool.grabbedCount+2]; $RecordTrajectoryBox => RecordPoints[tool.outline.first, LAST[NAT]]; $RecordSelectionBox => RecordPoints[tool.selected, tool.selectedCount]; $ReverseTrajectory => { IF tool.outline # NIL THEN { old: Trajectory _ tool.outline.first.rest; tool.outline.first.rest _ NIL; WHILE old # NIL DO temp: Trajectory _ old; old _ old.rest; temp.rest _ tool.outline.first.rest; tool.outline.first.rest _ temp ENDLOOP; }; ViewerOps.SetNewVersion[viewer]; }; $SelectPt => { GrabPoint[tool, markedPoint]; IF tool.selected # tool.grabbed THEN { tool.selected _ tool.grabbed; tool.selectedCount _ tool.grabbedCount; }; tool.grabbed _ NIL; tool.grabbedCount _ 0; }; $Smooth => { IF tool.selected # NIL THEN { list: LIST OF MarkedPoint _ tool.selected.rest; prev: MarkedPoint _ tool.selected.first; BBPoint[prev]; FOR i: NAT IN [0..tool.selectedCount) DO current: MarkedPoint _ list.first; next: MarkedPoint _ IF list.rest = NIL THEN tool.outline.first.rest.first ELSE list.rest.first; BBPoint[current]; current.x _ (2*current.x + prev.x + next.x)/4; current.y _ (2*current.y + prev.y + next.y)/4; BBPoint[current]; prev _ list.first; list.first _ current; list _ list.rest; IF list = NIL THEN list _ tool.outline.first.rest; ENDLOOP; BBPoint[list.first]; }; RecordBBox[]; ViewerOps.SetNewVersion[viewer]; }; $PaintAll, $TouchUp => { ViewerOps.PaintViewer[viewer, client, FALSE, action]; tool.paintRectangles _ NIL; }; $NewPt, $InvertGrabbed, $InvertSel, $EraseGrabbedPoint => { ViewerOps.PaintViewer[viewer, client, FALSE, action]; }; ENDCASE => ERROR; ENDLOOP; }; MenuAction: Menus.ClickProc = {Notify[NARROW[parent], LIST[clientData]]}; AddMenuItem: PUBLIC PROC [atom: ATOM] = { name: ROPE ~ Atom.GetPName[atom]; menu: Menus.Menu _ NARROW[viewerClass.menu]; ResetViewerMenu: PROC [v: Viewer] RETURNS [BOOL _ TRUE] ~ { IF v.class = viewerClass THEN ViewerOps.SetMenu[v, menu]; }; Menus.AppendMenuEntry[ menu: menu, entry: Menus.CreateEntry[ name: name, proc: MenuAction, clientData: atom, documentation: NIL ] ]; ViewerOps.EnumerateViewers[ResetViewerMenu]; }; CreateMenu: PROC RETURNS [Menus.Menu] = { menu: Menus.Menu _ Menus.CreateMenu[]; Menus.AppendMenuEntry[ menu: menu, entry: Menus.CreateEntry[ name: "Clear", proc: MenuAction, clientData: $Clear, documentation: "Clear the viewer" ] ]; Menus.AppendMenuEntry[ menu: menu, entry: Menus.CreateEntry[ name: "Refresh", proc: MenuAction, clientData: $Refresh, documentation: "Refresh the viewer" ] ]; Menus.AppendMenuEntry[ menu: menu, entry: Menus.CreateEntry[ name: "Save", proc: MenuAction, clientData: $Save, documentation: "Save the samples" ] ]; Menus.AppendMenuEntry[ menu: menu, entry: Menus.CreateEntry[ name: "Smooth", proc: MenuAction, clientData: $Smooth, documentation: "Smooth out the selected samples" ] ]; Menus.AppendMenuEntry[ menu: menu, entry: Menus.CreateEntry[ name: "Reverse", proc: MenuAction, clientData: $Reverse, documentation: "Reverse the order of the points in the current trajectory" ] ]; Menus.AppendMenuEntry[ menu: menu, entry: Menus.CreateEntry[ name: "Delete", proc: MenuAction, clientData: $Delete, documentation: "Delete the currently selected points" ] ]; RETURN [menu]; }; NewTIP: PUBLIC PROC = { ResetViewerTIP: PROC [v: Viewer] RETURNS [BOOL _ TRUE] ~ { IF v.class = viewerClass THEN v.tipTable _ viewerClass.tipTable; }; viewerClass.tipTable _ TIPUser.InstantiateNewTIPTable["SampledCurveEdit.TIP"]; ViewerOps.EnumerateViewers[ResetViewerTIP]; }; viewerClass: ViewerClasses.ViewerClass _ NEW[ViewerClasses.ViewerClassRec _ [ paint: PaintProc, notify: Notify, init: InitViewer, save: SaveProc, menu: CreateMenu[], tipTable: TIPUser.InstantiateNewTIPTable["SampledCurveEdit.TIP"] ]]; Break: PROC [char: CHAR] RETURNS [IO.CharClass] = { IF char = '_ OR char = '; THEN RETURN [break]; IF char = ' OR char = ' OR char = ', OR char = '\n THEN RETURN [sepr]; RETURN [other]; }; GetToken: PROC [stream: IO.STREAM, breakProc: IO.BreakProc] RETURNS [rope: ROPE _ NIL] ~ { rope _ stream.GetTokenRope[breakProc ! IO.EndOfStream => CONTINUE].token }; SampledCurveEditCommand: Commander.CommandProc ~ { stream: IO.STREAM _ IO.RIS[cmd.commandLine]; name: ROPE _ GetToken[stream, Break]; name _ FS.ExpandName[name ! FS.Error => {cmd.out.PutRope[error.explanation]; name _ NIL; CONTINUE}].fullFName; IF Rope.Length[name] = 0 THEN [] _ ViewerOps.CreateViewer[$SampledCurveEdit, [name: "Sampled Curve Editor [No File]", file: NIL]] ELSE [] _ ViewerOps.CreateViewer[$SampledCurveEdit, [name: Rope.Concat["Sampled Curve Editor ", name], file: name]]; }; ViewerOps.RegisterViewerClass[$SampledCurveEdit, viewerClass]; Commander.Register["SampledCurveEdit", SampledCurveEditCommand, "Create a viewer to edit a sampled curve"]; END. *SampledCurveEditImpl.mesa Copyright (C) 1983, Xerox Corporation. All rights reserved. Michael Plass, September 24, 1984 3:11:36 pm PDT Last Edited by: Stone, October 9, 1984 8:29:12 pm PDT The first trajectory in the outline is the one currently being edited. Points to the item before the point or points actually grabbed. Points to the item before the point actually selected. PROC [self: Viewer, context: Graphics.Context, whatChanged: REF ANY, clear: BOOL] List head is present iff DispatchProcess is alive. Records touchup box Records touchup box Records touchup box Will record boxes that need touchup Remove list head so Notify knows that DispatchProcess need restarting Returning NIL will cause this DispatchProcess to go away. In case an external client was waiting for access to the outline. Κ#ή˜Jšœ™J™Jšœœ ˜+Jšœ˜J˜—šŸœœ˜*Jš œœœœGœœ˜žJšœ œ ˜J˜šžœœ˜Jš œœœœœ˜Jšœ"˜"Jšœ œ˜Jšœœœ˜3Jšœ˜Jšœ˜—Jšœ œ'˜7šžœœ œF˜^šž œœœœ˜"šœœœ˜'Jšœ9˜9Jšœ˜Jšœ˜—Jšœ˜—Jš œ œœœœ˜Jšœ œœœ˜)šœ œœœ˜ šœœœ˜(Jšœ2™2—Jšœœ˜-Jšœ˜—š œœœœœœ˜>Jšœ˜Jšœ ˜ Jšœ˜—šœ˜Jšœ œ˜Jš œœœœœ˜Ošœœœ˜.šœœ˜JšœN˜NJšœœ˜—Jš˜Jšœ˜—Jšœœ˜—šœœ˜Jšœœ˜#Jšœœœ˜Jšœ˜J˜ Jšœ˜—šœœ˜$Jšœœ˜ Jšœ˜šœœ˜Jšœœ˜šœ%˜%Jšœ˜Jšœ*˜*Jšœ˜—šœ!˜!šœœœ˜Jšœ1˜1JšœP˜RJšœ2˜6Jšœ ˜Jšœ˜—Jšœ3œ˜?Jšœ˜—šœ˜Jšœ˜Jšœ˜—Jšœœ˜—šœ˜Jšœœ˜šœ ˜ J˜Jšœ˜—šœ ˜ Jšœœ˜Jšœ˜Jšœœ˜Jšœ˜Jšœœœ%˜=J˜ Jšœ˜—šœ˜Jšœ œœ˜+Jšœ'˜)Jšœ"œœ˜0Jšœ ˜Jšœ˜—šœ ˜ šœœœœ˜HJšœ,˜,Jšœ˜—Jšœœ'˜EJ˜ Jšœ˜—šœ ˜ Jšœ9œ˜=Jšœœ˜Jšœ+˜+J˜ J˜—šœ˜Jšœœ˜Jšœ˜Jšœ˜—šœ˜Jšœœ˜Jšœ˜Jšœœ˜Jšœ˜J˜—šœ˜š œœœœœ˜>Jšœ˜Jšœ!˜!Jšœœœ'˜KJ˜ Jšœ&˜&Jšœ+˜+J˜—Jšœœ˜#Jšœ+˜+J˜ Jšœ˜—šœ˜šœœœ˜Jšœ2˜2šœœœ˜AJšœ-˜-Jšœ˜—Jšœ˜—Jšœœ˜Jšœ˜Jšœœ˜Jšœ˜J˜ Jšœ˜—šœœœœ˜+Jšœœœœ˜Jšœœ˜Jšœ œœœ˜Jšœœœœ˜Jšœ˜Jšœœœ,˜=š œœœœ˜;šœœ˜Jšœœœ˜%Jšœ ˜ Jšœ˜—šœœ˜Jšœœœ˜&Jšœ"˜"Jšœ˜—Jšœœœ˜$Jšœ˜Jšœ ˜ Jšœ˜—Jšœ˜Jšœ˜Jšœœ˜Jšœ˜J˜ Jšœ˜—Jšœ(˜(šœ ˜ Jšœ0˜0Jšœ&˜&J˜ J˜—JšœE˜EJšœ9œœ˜DJšœG˜Gšœ˜šœœœ˜J˜*Jšœœ˜šœœ˜Jšœ˜Jšœ˜Jšœ$˜$Jšœ˜Jšœ˜—Jšœ˜—J˜ J˜—šœ˜Jšœ˜šœœ˜&Jšœ˜Jšœ'˜'Jšœ˜—Jšœœ˜Jšœ˜Jšœ˜—šœ ˜ šœœœ˜Jšœœœ"˜/Jšœ(˜(Jšœ˜šœœœ˜(Jšœ"˜"Jš œœ œœœ˜_Jšœ˜Jšœ.˜.Jšœ.˜.Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœœœ ˜2Jšœ˜—Jšœ˜Jšœ˜—J˜ J˜ J˜—šœ˜Jšœ&œ ˜5Jšœœ˜Jšœ˜—šœ;˜;Jšœ&œ ˜5Jšœ˜—Jšœœ˜—Jšœ˜—J˜J˜—šž œœ œ˜IJ˜—šž œœœœ˜)Jšœœ˜!Jšœœ˜,š žœœ œœœ˜;Jšœœ˜9Jšœ˜—šœ˜Jšœ ˜ ˜Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜J˜—J˜—Jšœ,˜,Jšœ˜J˜—šž œœœ˜)Jšœ&˜&šœ˜Jšœ ˜ ˜J˜Jšœ˜Jšœ˜J˜!J˜—J˜—šœ˜Jšœ ˜ ˜J˜Jšœ˜Jšœ˜J˜#J˜—J˜—šœ˜Jšœ ˜ ˜J˜ Jšœ˜Jšœ˜J˜!J˜—J˜—šœ˜Jšœ ˜ ˜J˜Jšœ˜Jšœ˜J˜0J˜—J˜—šœ˜Jšœ ˜ ˜Jšœ˜Jšœ˜Jšœ˜JšœJ˜JJ˜—J˜—šœ˜Jšœ ˜ ˜Jšœ˜Jšœ˜Jšœ˜Jšœ5˜5J˜—J˜—Jšœ˜Jšœ˜J˜—šžœœœ˜š žœœ œœœ˜:Jšœœ#˜@Jšœ˜—JšœN˜NJšœ+˜+Jšœ˜J˜—šœ)œ!˜MJšœ˜Jšœ˜J˜J˜Jšœ˜Jšœ@˜@J˜J˜—– "cedar" styleš žœœœœœ˜3Jšœ œ œœ ˜.Jš œ œ œ œ œœ˜HJšœ ˜Jšœ˜J˜—šžœœ œœ œ œœœ˜ZJšœ'œœ˜HJšœ˜J˜—šžœ˜2Jš œœœœœ˜,Jšœœ˜%Jš œœœ6œœ ˜nšœœ˜Jšœ^œ˜c—š˜Jšœo˜o—Jšœ˜J˜—Jšœ>˜>Jšœk˜kJ˜Jšœ˜J˜J˜—…—dŽ‹–