<<>> <> <> <> <> DIRECTORY BasicTime, CedarProcess, Commander, Controls, Convert, Draw2d, FileNames, FS, G2dBasic, G2dPopUp, G2dSpline, G2dTool, G2dZoom, Icons, Imager, ImagerBackdoor, ImagerMaskCache, ImagerSample, ImagerTransformation, IO, MessageWindow, Process, Real, RealFns, Rope, Vector2, ViewerClasses, ViewerOps, ViewerPrivate, ViewerTools; G2dZoomCmdImpl: CEDAR PROGRAM IMPORTS BasicTime, CedarProcess, Controls, Convert, Draw2d, FileNames, FS, G2dPopUp, G2dSpline, G2dTool, G2dZoom, Icons, ImagerBackdoor, ImagerMaskCache, ImagerSample, ImagerTransformation, IO, MessageWindow, Process, Real, RealFns, Rope, ViewerOps, ViewerPrivate, ViewerTools ~ BEGIN <> PixelMap: TYPE ~ Imager.PixelMap; SampleMap: TYPE ~ ImagerSample.SampleMap; Transformation: TYPE ~ ImagerTransformation.Transformation; ROPE: TYPE ~ Rope.ROPE; VEC: TYPE ~ Vector2.VEC; Viewer: TYPE ~ ViewerClasses.Viewer; Key: TYPE ~ REF KeyRep; KeyRep: TYPE ~ RECORD [keyNum: NAT ¬ 0, time, dx, dy, scale, rotate: REAL ¬ 0]; KeySequence: TYPE ~ REF KeySequenceRep; KeySequenceRep: TYPE ~ RECORD [ length: CARDINAL ¬ 0, element: SEQUENCE maxLength: CARDINAL OF Key ]; Frame: TYPE ~ RECORD [bw, a, b: SampleMap]; FrameSequence: TYPE ~ REF FrameSequenceRep; FrameSequenceRep: TYPE ~ RECORD [ length: CARDINAL ¬ 0, element: SEQUENCE maxLength: CARDINAL OF Frame ]; Data: TYPE ~ REF DataRep; DataRep: TYPE ~ RECORD [ operation: ROPE ¬ NIL, stop, busy: BOOL ¬ FALSE, keys: KeySequence ¬ NIL, nFrame: INT ¬ -1, keyNum, selectedKey: NAT ¬ 0, spline: G2dSpline.Spline1dSequence ¬ NIL, knots: G2dBasic.RealSequence ¬ NIL, ticks: SampleMap ¬ NIL, frames: FrameSequence ¬ NIL, xMove, yMove: Controls.Control ¬ NIL, scale, rotate: Controls.Control ¬ NIL, speed, frameNum: Controls.Control ¬ NIL, outerData: Controls.OuterData ¬ NIL, outer, viewer, graphics: Viewer ¬ NIL ]; <> ZoomCmd: Commander.CommandProc ~ { d: Data ¬ NEW[DataRep]; d.xMove ¬ Controls.NewControl[name: "XMove", proc: XForm, type: hSlider, clientData: d, min: -10., max: 10.0, init: 0, x: 70, y: 15, w: 150, detents: LIST[[, 0.0]]]; d.yMove ¬ Controls.NewControl[name: "YMove", proc: XForm, type: vSlider, clientData: d, min: -10., max: 10.0, init: 0, x: 30, y: 15, h: 150, detents: LIST[[, 0.0]]]; d.scale ¬ Controls.NewControl[name: "Scale", proc: XForm, type: vSlider, taper: exp, clientData: d, min: 1.0/32.0, max: 32., init: 1, x: 235, y: 15, h: 150, detents: LIST[[, 0.5], [, 1.0], [, 2.0]]]; d.rotate ¬ Controls.NewControl[name: "Rotate", proc: XForm, type: dial, clientData: d, max: 360.0, init: 0.0, x: 100, y: 65, w: 100, h: 100, detents: LIST[[, 0.0]]]; d.speed ¬ Controls.NewControl[name: "Speed", type: vSlider, clientData: d, min: 1, max: 40, init: 30, x: 300, y: 15, h: 150, precision: 0, detents: LIST[[, 30]]]; d.frameNum ¬ Controls.NewControl[name: "Frame", proc: NFrame, type: vSlider, clientData: d, min: 0, max: 0, init: 0, x: 345, y: 15, h: 150, precision: 0]; d.outerData ¬ Controls.OuterViewer[ name: "2dZoom", column: right, buttons: LIST[ Controls.ClickButton[name: "Play:", row: 0], Controls.ClickButton[name:"STOP", proc:Button, clientData:d, guarded:TRUE, row:0, x:45], Controls.ClickButton["Interp", Button, d, 0], Controls.ClickButton["Replay", Button, d, 0], Controls.ClickButton["Cycle", Button, d, 0], Controls.ClickButton["BackForth", Button, d, 0], Controls.ClickButton["Time", Button, d, 0], Controls.ClickButton[name: "Keys:", row: 1], Controls.ClickButton["Add", Button, d, 1, 45], Controls.ClickButton["Del", Button, d, 1], Controls.ClickButton["Del All", Button, d, 1], Controls.ClickButton["Write", Button, d, 1], Controls.ClickButton["Read", Button, d, 1], Controls.ClickButton[name: " ", row: 2], Controls.ClickButton[name:"Get Viewer", proc:Button, clientData:d, guarded:TRUE, row:2, x:45], Controls.ClickButton[name: "Reset", proc: Button, clientData: d, guarded: TRUE, row: 2], Controls.ClickButton["IP Out", Button, d, 2], Controls.ClickButton["AISs Out", Button, d, 2], Controls.ClickButton["Help!", Button, d, 2]], controls: LIST[d.rotate, d.scale, d.xMove, d.yMove, d.speed, d.frameNum], typescriptHeight: 18, graphicsHeight: 105, drawProc: DrawProc, mouseProc: MouseProc, destroyProc: DestroyProc, clientData: d ]; d.outer ¬ d.outerData.parent; d.graphics ¬ d.outerData.graphics; <> <<[] _ ImagerMaskCache.GetNamedCache[$Bitmap, 8000]; -- this doesn't work>> [] ¬ ImagerMaskCache.GetNamedCache[$Bitmap]; [] ¬ ViewerPrivate.SetCreator[NIL]; TRUSTED {Process.Detach[FORK CycleIcon[d.outer]]}; }; CycleIcon: PROC [v: Viewer] ~ { PaintIcon: PROC [i: NAT] ~ { v.icon ¬ icons[i]; ViewerOps.PaintViewer[v, all]; Process.Pause[Process.MsecToTicks[100]]; }; v.icon ¬ icons[0]; WHILE NOT v.destroyed DO IF forwardIcon THEN FOR i: NAT IN [0..nIcons) DO IF v.iconic THEN PaintIcon[i]; ENDLOOP ELSE FOR i: NAT DECREASING IN [0..nIcons) DO IF v.iconic THEN PaintIcon[i]; ENDLOOP; forwardIcon ¬ NOT forwardIcon; Process.Pause[Process.SecondsToTicks[7]]; ENDLOOP; }; forwardIcon: BOOL ¬ TRUE; DestroyProc: Controls.DestroyProc ~ { d: Data ¬ NARROW[clientData]; d.stop ¬ TRUE; G2dZoom.Release[d.viewer]; }; Message: PROC [rope: ROPE, clear, blink: BOOL ¬ TRUE] ~ { MessageWindow.Append[Rope.Concat["\t\t\t", rope], clear]; IF blink THEN MessageWindow.Blink[]; }; <> XFromTime: PROC [d: Data, time: REAL] RETURNS [INTEGER] ~ { t: REAL ¬ IF d.keys = NIL OR d.keys[0].time = d.keys[d.keys.length-1].time THEN 0.5 ELSE (time-d.keys[0].time)/REAL[d.keys[d.keys.length-1].time-d.keys[0].time]; RETURN[Real.Round[25+t*(d.graphics.cw-50)]]; }; TimeFromX: PROC [d: Data, x: INTEGER] RETURNS [REAL] ~ { k: KeySequence ¬ d.keys; RETURN[SELECT TRUE FROM k = NIL OR k.length = 0 => 0.0, k.length = 1 OR d.graphics.cw = 0 => k[0].time, ENDCASE => (REAL[x-25]/(d.graphics.cw-50))*(k[k.length-1].time-k[0].time)+k[0].time]; }; Repaint: PROC [d: Data, whatChanged: REF ¬ NIL] ~ { ViewerOps.PaintViewer[d.graphics, client, FALSE, whatChanged]; }; DrawProc: Controls.DrawProc ~ { BufferTicks: PROC [op: {save, restore}] ~ { Save: PROC [pixelMap: PixelMap] ~ {d.ticks ¬ ImagerSample.Copy[pixelMap[0]]}; Restore: PROC [pixelMap: PixelMap] ~ {ImagerSample.Transfer[pixelMap[0], d.ticks]}; r: Imager.Rectangle ¬ [0, 0, d.graphics.ww, d.graphics.wh]; ImagerBackdoor.AccessBufferRectangle[context, IF op = save THEN Save ELSE Restore, r]; }; DrawTicks: PROC ~ { IF d.keys # NIL AND d.keys.length > 0 AND d.graphics.cw # 0 THEN { KeyX: PROC [n: NAT] RETURNS [x: REAL] ~ {x ¬ XFromTime[d, d.keys[n].time]}; lastT: REAL ¬ d.keys[d.keys.length-1].time; totalT: REAL ¬ lastT-d.keys[0].time; dT: REAL ¬ totalT*(80.0/d.graphics.cw); Draw2d.Line[context, [KeyX[0], 55], [KeyX[d.keys.length-1], 55]]; FOR n: NAT IN [0..d.keys.length) DO x: REAL ¬ KeyX[n]; Draw2d.Line[context, [x, 55], [x, 70]]; Draw2d.Label[context, [x-10, 75], IO.PutFR1["K%g", IO.int[d.keys[n].keyNum]]]; ENDLOOP; IF dT # 0.0 THEN { exp: INTEGER ¬ Real.Floor[RealFns.Log[10.0, totalT]]; div: REAL ¬ IF exp IN [-2..2] THEN 1.0 ELSE RealFns.Power[10.0, exp]; FOR t: REAL ¬ d.keys[0].time, t+dT WHILE t <= lastT DO tt: REAL ¬ IF lastT-t < dT THEN lastT ELSE t; x: REAL ¬ XFromTime[d, tt]; Draw2d.Line[context, [x, 40], [x, 55]]; Draw2d.Label[context, [x-10, 30], IO.PutFR1["%3.2f", IO.real[tt/div]]]; ENDLOOP; Draw2d.Label[context, [d.graphics.cw/2-40, 10], Rope.Concat[ "Time", IF div = 1.0 THEN NIL ELSE IO.PutFR1[" (X %6.4f)", IO.real[div]]]]; }; }; }; d: Data ¬ NARROW[clientData]; SELECT whatChanged FROM $Ticks => BufferTicks[restore]; ENDCASE => { Draw2d.DoWithBuffer[context, DrawTicks]; BufferTicks[save]; }; IF d.frames # NIL AND d.frames.length > 1 AND d.nFrame # -1 THEN { totalT: REAL ¬ d.keys[d.keys.length-1].time-d.keys[0].time; IF totalT # 0.0 THEN { x: REAL ¬ XFromTime[d, d.keys[0].time+totalT*(REAL[d.nFrame]/(d.frames.length-1))]; Draw2d.Line[context, [x, 55], [x, 85]]; Draw2d.Label[context, [x-10, 90], IO.PutFR1["F%g", IO.int[d.nFrame ]]]; }; }; }; MouseProc: Controls.MouseProc ~ { d: Data ¬ NARROW[clientData]; time: REAL ¬ TimeFromX[d, mouse.pos.x]; IF d.keys = NIL OR d.keys.length = 0 THEN RETURN; SELECT mouse.state FROM down => { close: REAL ¬ Real.LargestNumber; FOR n: NAT IN [0..d.keys.length) DO IF ABS[d.keys[n].time-time] > close THEN LOOP; close ¬ ABS[d.keys[n].time-time]; d.selectedKey ¬ n; ENDLOOP; }; held => { d.keys[d.selectedKey].time ¬ time; OrderKeys[d.keys]; Repaint[d]; }; ENDCASE; }; <> Interp: PROC [d: Data] ~ { IF d.keys # NIL AND d.keys.length > 0 THEN { reply: ROPE ¬ Controls.TypescriptRead[d.outerData.typescript, "Number of frames: "]; IF reply # NIL THEN { nFrames: CARDINAL ¬ Convert.IntFromRope[reply ! Convert.Error => GOTO Bad]; IF nFrames > 2 THEN { time0: REAL ¬ d.keys[0].time; time1: REAL ¬ d.keys[d.keys.length-1].time; d.frameNum.max ¬ nFrames-1; IF d.frames = NIL OR d.frames.maxLength < nFrames THEN d.frames ¬ NEW[FrameSequenceRep[nFrames]]; d.frames.length ¬ nFrames; FOR n: INT IN [0..nFrames) WHILE NOT d.stop DO SetTime[d, time0+(n/REAL[nFrames-1])*(time1-time0)]; Buffer[d, save, n]; ENDLOOP; IF d.stop THEN d.frames.length ¬ 0; }; }; EXITS Bad => Message["Bad value"]; }; }; OrderKeys: PROC [keys: KeySequence] ~ { Success: PROC RETURNS [BOOL ¬ TRUE] ~ { FOR n: NAT IN [1..keys.length) DO IF keys[n-1].time > keys[n].time THEN { temp: Key ¬ keys[n]; keys[n] ¬ keys[n-1]; keys[n-1] ¬ temp; RETURN[FALSE]; }; ENDLOOP; }; DO IF Success[] THEN EXIT; ENDLOOP; }; GetInterpReady: PROC [d: Data] ~ { IF d.spline = NIL OR d.spline.maxLength < d.keys.length THEN d.spline ¬ NEW[G2dSpline.Spline1dSequenceRep[d.keys.length-1]]; IF d.knots = NIL OR d.knots.maxLength < d.keys.length THEN d.knots ¬ NEW[G2dBasic.RealSequenceRep[d.keys.length]]; d.knots.length ¬ d.keys.length; }; SetTime: PROC [d: Data, time: REAL] ~ { SetFromKey: PROC [k: Key] ~ {rotate ¬ k.rotate; scale ¬ k.scale; dx ¬ k.dx; dy ¬ k.dy}; rotate, scale, dx, dy: REAL; GetInterpReady[d]; FOR n: NAT IN [0..d.keys.length) DO IF d.keys[n].time < time THEN LOOP; IF n = 0 THEN SetFromKey[d.keys[0]] ELSE { Field: TYPE ~ {rotate, scale, dx, dy}; GetValue: PROC [k: Key, f: Field] RETURNS [r: REAL] ~ { r ¬ SELECT f FROM dx => k.dx, dy => k.dy, scale => k.scale, ENDCASE => k.rotate; }; GetReal: PROC [f: Field] RETURNS [r: REAL] ~ { FOR n: NAT IN [0..d.keys.length) DO d.knots[n] ¬ GetValue[d.keys[n], f]; ENDLOOP; d.spline ¬ G2dSpline.Interpolate1d[d.knots, d.spline, TRUE]; r ¬ G2dSpline.Position1d[d.spline[n-1], t]; }; t: REAL ¬ (time-d.keys[n-1].time)/(d.keys[n].time-d.keys[n-1].time); rotate ¬ GetReal[rotate]; scale ¬ GetReal[scale]; dx ¬ GetReal[dx]; dy ¬ GetReal[dy]; EXIT; }; REPEAT FINISHED => SetFromKey[d.keys[d.keys.length-1]]; ENDLOOP; DoTransform[d, dx, dy, scale, rotate, FALSE]; }; Buffer: PROC [d: Data, op: {save, restore}, nFrame: NAT] ~ { <> <> <> <> <> <> < 1, 8 => 8, ENDCASE => 24;>> <> <> <> <> <> <> <> <> <> <> <<1 => [ImagerSample.Copy[clipped], NIL, NIL],>> <<8 => [NIL, ImagerSample.Copy[clipped], NIL],>> < [NIL, ImagerSample.Copy[clipped], ImagerSample.Copy[clippedB]] >> <> <> < f.bw = NIL, 8 => f.b # NIL, ENDCASE => f.b = NIL) THEN {>> <> <> <> <<};>> <