<> <> DIRECTORY Atom, Commander, SampledCurveEdit, InputFocus, Graphics, IO, Menus, MessageWindow, Process, Rope, TIPUser, ViewerClasses, ViewerOps ; SampledCurveEditImpl: CEDAR MONITOR IMPORTS Atom, Commander, InputFocus, Graphics, IO, Menus, MessageWindow, Process, Rope, 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; }; 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; 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]; }; $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[]; }; 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[]; }; }; outline: Outline => { tool.outline _ outline; }; ENDCASE => ERROR; SELECT action FROM $NothingMoreToDo => NULL; $StartPt => { tool.selected _ NIL; tool.selectedCount _ 0; tool.grabbed _ NIL; tool.grabbedCount _ 0; tool.outline _ CONS[LIST[header, markedPoint], tool.outline]; }; $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]; }; $Clear => { tool.outline.first.rest _ tool.grabbed _ tool.selected _ NIL; tool.outline.rest _ NIL; tool.grabbedCount _ tool.selectedCount _ 0; }; $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; }; $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; }; $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; }; $GrabPt => GrabPoint[tool, markedPoint]; $MoveTo => { markedPoint.kind _ tool.grabbed.rest.first.kind; tool.grabbed.rest.first _ markedPoint; }; $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; }; }; $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[] }; $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: "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, 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]; IF Rope.Length[name] = 0 THEN [] _ ViewerOps.CreateViewer[$SampledCurveEdit, [name: "Sampled Curve Editor [No File]", file: NIL]] ELSE [] _ ViewerOps.CreateViewer[$SampledCurveEdit, [name: name, file: name]]; }; ViewerOps.RegisterViewerClass[$SampledCurveEdit, viewerClass]; Commander.Register["SampledCurveEdit", SampledCurveEditCommand, "Create a viewer to edit a sampled curve"]; END.