CtWarpCommandsImpl:
CEDAR
PROGRAM
IMPORTS Buttons, CtBasic, CtDispatch, CtMisc, CtMod, CtViewer, G2dContour, G2dBasic, ImagerSample, IO, MessageWindow, Process, Real, RuntimeError, TypeScript, ViewerIO, ViewerOps, ViewerSpecs, ViewerTools
PerspWarp Command
Persp: TYPE ~ REF PerspRep;
PerspRep:
TYPE ~
RECORD [
parent: Viewer ¬ NIL, -- parent viewer
image: Viewer ¬ NIL, -- the viewer to warp
button: CtViewer.MouseButton ¬ left,
box: ImagerSample.Box ¬ [[0, 0], [0, 0]], -- texture box
texture: PixelArray ¬ NIL, -- array of chosen texture
smooth: BOOL ¬ FALSE, -- anti-alias or not?
index: NAT ¬ 0, -- index to warp quadilateral points
points: ARRAY [0..4) OF IntegerPair -- warp quadrilateral
];
ctPerspWarpUsage:
ROPE ~ "Ct PerspWarp: Warp a window into a quadrilateral.";
CtPerspWarp: CtProc ~ {
v: Viewer ¬ ViewerOps.FindViewer["Ct PerspWarp"];
d: Persp ¬ IF v = NIL THEN NEW[PerspRep] ELSE NARROW[v.data];
d.image ¬ viewer;
IF v = NIL THEN TRUSTED {Process.Detach[FORK MakePersper[d]]};
affect ¬ [[0, 0], [0, 0]];
CtViewer.RegisterMouse[viewer, PerspMouse, d];
};
MakePersper:
PROC [d: Persp] ~ {
d.parent ¬ MakeViewer["Ct PerspWarp", $CtPerspWarp, 51, d];
ViewerTools.SetContents[ViewerTools.MakeNewTextViewer[
info: [scrollable: FALSE, parent: d.parent, border: FALSE, wx: 7, wy: 0, ww: 400, wh: 16]],
"Left: pick texture, Middle: warp points, Right: restart"];
Buttons.SetDisplayStyle[Buttons.Create[
[parent: d.parent, name: "Warp-On ", wx: 3, wy: 20], PerspWarpTog, d], $WhiteOnBlack];
Buttons.SetDisplayStyle[Buttons.Create[
[parent: d.parent, name: "Smooth-Off ", wx: 70, wy: 20], SmoothTog, d], $BlackOnWhite];
};
PerspWarpTog: Buttons.ButtonProc ~ {
d: Persp ¬ NARROW[clientData];
CtMisc.ButtonToggle[parent];
IF CtMisc.ButtonOn[parent]
THEN CtViewer.RegisterMouse[d.image, PerspMouse, d]
ELSE CtViewer.UnregisterMouse[d.image, PerspMouse];
};
SmoothTog: Buttons.ButtonProc ~ {
d: Persp ¬ NARROW[clientData];
CtMisc.ButtonToggle[parent];
d.smooth ¬ CtMisc.ButtonOn[parent]
};
PerspMouse: CtViewer.MouseProc ~ {
d: Persp ¬ NARROW[clientData];
IF mouse.state # down THEN RETURN;
IF (d.button ¬ mouse.button) = left
THEN d.box ¬ CtViewer.GetBoundingBox[viewer, [mouse.pos.y, mouse.pos.x]];
CtViewer.DoWithViewer[viewer, DoPersp, d];
};
DoPersp: CtViewer.ViewerProc ~ {
d: Persp ¬ NARROW[clientData];
CtViewer.DoWhileMouseDown[viewer, NIL];
SELECT d.button
FROM
left =>
d.texture ¬ CtBasic.PixelArrayFromSampleMap[ImagerSample.Clip[maps[0].map, d.box]];
middle => {
d.points[d.index] ¬ CtViewer.GetMousePos[viewer];
IF (d.index ¬ (d.index+1)
MOD 4) = 0
THEN
[] ¬ CtMod.PerspWarp[d.texture, maps, d.smooth, d.points];
};
ENDCASE => CtBasic.TransferPixelArrayToMap[d.texture, maps[0].map];
};
PerspWarpDestroy: ViewerClasses.DestroyProc ~ {
CtViewer.UnregisterMouse[NARROW[self.data, Persp].image, PerspMouse];
};
CoonsWarp Command
CoonsControl:
TYPE ~ CtMod.CoonsControl;
Coons: TYPE ~ REF CoonsRep;
CoonsRep:
TYPE ~
RECORD [
-- info for Coons mouse and warp procs
flag: ATOM ¬ NIL,
mouse: CtViewer.Mouse ¬ [],
cmd: Commander.Handle ¬ NIL, -- Commander handle
control: REF CoonsControl ¬ NIL, -- control during warping
r, g, b, bw: PixelArray, -- source picture (8 or 24 bpp)
ind: [0..4] ¬ 0, -- which curve are we editing? [0..4)
curves: ARRAY [0..4) OF PairSequence, -- the four outline curves
image: Viewer ¬ NIL, -- the viewer to warp
parent: Viewer ¬ NIL, -- the parent viewer
text: Viewer ¬ NIL, -- log's viewer, for feedback
filterButton: Viewer ¬ NIL, -- button for filter mode
lifeButton: Viewer ¬ NIL, -- to put CoonsWarp to sleep or wake up
awake: BOOL ¬ TRUE, -- awake or asleep?
s: IO.STREAM ¬ NIL -- log's output stream
];
ctCoonsWarpUsage:
ROPE ~ "Ct CoonsWarp: Warp an outlined area into a rectangle.";
CtCoonsWarp: CtProc ~ {
v: Viewer ¬ ViewerOps.FindViewer["Ct CoonsWarp"];
d: Coons ¬ IF v = NIL THEN NEW[CoonsRep] ELSE NARROW[v.data];
affect ¬ [[0, 0], [0, 0]];
d.cmd ¬ cmd;
d.image ¬ viewer;
IF v = NIL THEN TRUSTED {Process.Detach[FORK MakeCoonser[d]]};
IF maps.bpp = 24
THEN {
d.r ¬ CtBasic.PixelArrayFromSampleMap[maps[0].map];
d.g ¬ CtBasic.PixelArrayFromSampleMap[maps[1].map];
d.b ¬ CtBasic.PixelArrayFromSampleMap[maps[2].map];
}
ELSE d.bw ¬ CtBasic.PixelArrayFromSampleMap[maps[0].map];
CtViewer.RegisterMouse[viewer, CoonsMouse, d];
};
MakeCoonser:
PROC [d: Coons] ~ {
d.parent ¬ MakeViewer["Ct CoonsWarp", $CtCoonsWarp, 120, d];
d.text ¬ TypeScript.Create[
[parent: d.parent, border: FALSE, wx: 0, wy: 0, ww: ViewerSpecs.openRightWidth, wh: 85]];
d.s ¬ ViewerIO.CreateViewerStreams[NIL, d.text].out;
Buttons.SetDisplayStyle[d.lifeButton ¬ Buttons.Create[
[parent: d.parent, name: "AWAKE ", wx: 3, wy: 90], CoonsAwakeTog, d], $WhiteOnBlack];
[] ¬ Buttons.Create[[parent: d.parent, name: "Abort", wx: 50, wy: 90], CoonsAbort, d,,,, TRUE];
[] ¬ Buttons.Create[[parent: d.parent, name: "Restart", wx: 95, wy: 90], CoonsRestart, d];
[] ¬ Buttons.Create[[parent: d.parent, name: "Smooth", wx: 145, wy: 90], CoonsSmooth, d];
d.filterButton ¬ Buttons.Create[
[parent: d.parent, name: "Filter: point", wx: 240, wy: 90], CoonsFilter, d];
IO.PutRope[d.s, "\nPlease wait . . . "];
FOR i: INT IN [0..4) DO d.curves[i] ¬ NEW[G2dBasic.PairSequenceRep[64]]; ENDLOOP; -- grow?
d.control ¬ NEW[CoonsControl];
IO.PutRope[d.s, "\nSTART DRAWING: L: draw outline, M: warp, R: restore original\n\n"];
};
CoonsSmooth: Buttons.ButtonProc ~ {
d: Coons ¬ NARROW[clientData];
CoonsCurvesDraw[d, $Undraw];
FOR j:
NAT
IN [1..10]
DO
FOR i: NAT IN [0..4) DO [] ¬ G2dContour.SmoothPairs[d.curves[i], d.curves[i]]; ENDLOOP;
ENDLOOP;
CoonsCurvesDraw[d, $Draw];
};
CoonsRestart: Buttons.ButtonProc ~ {
NARROW[clientData, Coons].ind ¬ 0};
CoonsAbort: Buttons.ButtonProc ~ {
NARROW[clientData, Coons].control.abort ¬
TRUE};
CoonsAwakeTog: Buttons.ButtonProc ~ {
d: Coons ¬ NARROW[clientData];
Buttons.ReLabel[d.lifeButton, IF d.awake ¬ NOT d.awake THEN "AWAKE" ELSE "ASLEEP"];
Buttons.SetDisplayStyle[d.lifeButton, IF d.awake THEN $WhiteOnBlack ELSE $BlackOnWhite];
IF d.awake
THEN CtViewer.RegisterMouse[d.image, CoonsMouse, d]
ELSE CtViewer.UnregisterMouse[d.image, CoonsMouse];
};
CoonsFilter: Buttons.ButtonProc ~ {
d: Coons ¬ NARROW[clientData];
d.control.filter ¬ IF d.control.filter = point THEN box ELSE point;
Buttons.ReLabel[d.filterButton, IF d.control.filter = point THEN "Filter: point" ELSE "Filter: box"];
};
CoonsWarpDestroy: ViewerClasses.DestroyProc ~ {
d: Coons ¬ NARROW[self.data];
d.s ¬ d.cmd.out;
d.control.abort ¬ TRUE;
CtViewer.UnregisterMouse[d.image, CoonsMouse];
};
CoonsCurvesDrawAction: CtViewer.ViewerProc ~ {
d: Coons ¬ NARROW[clientData];
FOR c:
NAT
IN [0..4)
DO
FOR i:
NAT
IN [0..d.curves[c].length)
DO
CoonsDotDraw[d, maps, Real.Fix[d.curves[c][i].x], Real.Fix[d.curves[c][i].y], d.flag];
ENDLOOP;
ENDLOOP;
};
CoonsCurvesDraw:
PROC [d: Coons, flag:
ATOM] ~ {
d.flag ¬ flag;
CtViewer.DoWithViewer[d.image, CoonsCurvesDrawAction, d];
};
CoonsDotDraw:
PROC [d: Coons, maps: SampleMaps, x, y:
NAT, flag:
ATOM] ~ {
x0: NAT ¬ x-1;
x1: NAT ¬ x+1;
y0: NAT ¬ y-1;
y1: NAT ¬ y+1;
SELECT flag
FROM
$Draw =>
SELECT maps.bpp
FROM
8 => ImagerSample.Fill[maps[0].map, [[y0, x0], [y1, x1]], 255];
24 => CtBasic.PutRGBBox[maps, x0, y0, x1, y1, [255, 255, 255]];
ENDCASE;
$Undraw =>
SELECT maps.bpp
FROM
8 => CtBasic.PixelArrayBoxToMap[d.bw, maps[0].map, x0, y0, x1, y1];
24 => {
CtBasic.PixelArrayBoxToMap[d.r, maps[0].map, x0, y0, x1, y1];
CtBasic.PixelArrayBoxToMap[d.g, maps[1].map, x0, y0, x1, y1];
CtBasic.PixelArrayBoxToMap[d.b, maps[2].map, x0, y0, x1, y1];
};
ENDCASE;
ENDCASE;
};
CoonsMouseAction: CtViewer.ViewerProc ~ {
d: Coons ¬ NARROW[clientData];
SELECT d.mouse.button
FROM
middle => {
-- warp
d.control.abort ¬ FALSE;
IF d.ind # 0
THEN Blink["Four boundary curves needed"]
ELSE {
CtMod.CoonsWarp[d.r, d.g, d.b, d.curves, maps, d.control];
IO.PutRope[d.s, " done!\n"];
};
};
right =>
-- restore original image
SELECT maps.bpp
FROM
8 => CtBasic.TransferPixelArrayToMap[d.bw, maps[0].map];
24 => {
CtBasic.TransferPixelArrayToMap[d.r, maps[0].map];
CtBasic.TransferPixelArrayToMap[d.g, maps[1].map];
CtBasic.TransferPixelArrayToMap[d.b, maps[2].map];
};
ENDCASE;
left => {
-- add point(s)
AddPoint:
PROC [pos: IntegerPair] ~ {
d.curves[d.ind] ¬ G2dBasic.AddToPairSequence[d.curves[d.ind], [pos.x, pos.y]
! RuntimeError.BoundsFault => GOTO Bad]; -- record the point in curve array
CoonsDotDraw[d, maps, pos.x, pos.y, $Draw]; -- draw dots on screen
EXITS Bad => {
Blink[
IO.PutFR["Curve %g length exceeded at %g points",
IO.int[d.ind], IO.int[d.curves[d.ind].length]]];
d.curves[d.ind].length ¬ 0; -- best can do: restart curve
};
};
Poll: CtViewer.PollProc ~ {AddPoint[pos]};
CtViewer.UnregisterMouse[viewer, CoonsMouse];
IO.PutF1[d.s, "curve %g: ", IO.int[d.ind]]; -- assume just had a mouse-down
d.curves[d.ind].length ¬ 0;
AddPoint[d.mouse.pos];
CtViewer.DoWhileMouseDown[viewer, Poll];
IO.PutF1[d.s, "%g pts, ", IO.int[d.curves[d.ind].length]];
IF d.curves[d.ind].length < 2
THEN {
Blink[
IO.PutFR["Too few points (%g), redraw curve %g",
IO.int[d.curves[d.ind].length], IO.int[d.ind]]];
d.curves[d.ind].length ¬ 0; -- best can do: restart curve
IO.PutF1[d.s, "curve %g: ", IO.int[d.ind]];
}
ELSE d.ind ¬ (d.ind+1) MOD 4;
CtViewer.RegisterMouse[viewer, CoonsMouse, d];
};
ENDCASE;
};
CoonsMouse: CtViewer.MouseProc ~ {
d: Coons ¬ NARROW[clientData];
d.mouse ¬ mouse;
CtViewer.DoWithViewer[viewer, CoonsMouseAction, d];
};
Start Code
ViewerOps.RegisterViewerClass[
$CtPerspWarp, NEW[ViewerClassRec ¬ [destroy: PerspWarpDestroy]]];
ViewerOps.RegisterViewerClass[
$CtCoonsWarp, NEW[ViewerClassRec ¬ [destroy: CoonsWarpDestroy]]];
CtDispatch.RegisterCtOp["Warp:", NIL, NIL];
CtDispatch.RegisterCtOp["PerspWarp", CtPerspWarp, ctPerspWarpUsage];
CtDispatch.RegisterCtOp["CoonsWarp", CtCoonsWarp, ctCoonsWarpUsage];