SampledCurveEditImpl.mesa
Michael Plass, November 8, 1983 5:32 pm
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];
Tool: TYPE = REF ToolRec;
ToolRec:
TYPE =
RECORD [
actionQueue: LIST OF REF,
actionQueueEmpty: CONDITION,
outline:
LIST
OF Trajectory,
The first trajectory in the outline is the one currently being edited.
grabbed:
LIST
OF MarkedPoint,
Points to the item before the point or points actually grabbed.
grabbedCount: NAT,
selected:
LIST
OF MarkedPoint ←
NIL,
Points to the item before the point actually selected.
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
PROC [self: Viewer, context: Graphics.Context, whatChanged: REF ANY, clear: BOOL]
= {
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];
List head is present iff DispatchProcess is alive.
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];
Records touchup box
QueueTouchUp[];
};
$Refresh => QueuePaintAll[];
$Reverse => {
Queue[$InvertSel, $AutoInverse];
Queue[$ClearSel, $Idempotent];
Queue[$ReverseTrajectory, $AutoInverse];
};
$Smooth => {
Queue[$Smooth];
Records touchup box
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];
Records touchup box
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];
Will record boxes that need touchup
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;
Remove list head so Notify knows that DispatchProcess need restarting
ref ←
NIL;
Returning NIL will cause this DispatchProcess to go away.
NOTIFY tool.actionQueueEmpty;
In case an external client was waiting for access to the outline.
}
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.