<> <> <> <> <<>> <<>> DIRECTORY AIS USING [FRef, OpenFile, Raster, ReadRaster, CloseFile], AISViewer, FS USING [ExpandName], Imager USING [Context, DoSaveAll, ConcatT, Rectangle, SetColor, MaskStrokeTrajectory, SetSampledColor, MaskBox], ImagerBackdoor USING [GetBounds, invert], ImagerColorOperator USING [ColorOperator, GrayLinearColorModel], ImagerPath USING [Trajectory, MoveTo, LineTo], ImagerPixelArray USING [PixelArray, FromAIS], ImagerTransformation USING [Scale], Real USING [RoundC], Rope USING [ROPE], TIPUser USING [InstantiateNewTIPTable, TIPScreenCoords], ViewerClasses USING [PaintProc, Viewer, ViewerClass, ViewerRec, ViewerClassRec, InitProc, NotifyProc], ViewerOps USING [CreateViewer, PaintViewer, RegisterViewerClass]; AISViewerImpl: CEDAR MONITOR IMPORTS AIS, FS, Imager, ImagerBackdoor, ImagerColorOperator, ImagerPath, ImagerPixelArray, ImagerTransformation, Real, TIPUser, ViewerOps EXPORTS AISViewer = BEGIN OPEN AISViewer; ROPE: TYPE ~ Rope.ROPE; ColorOp: TYPE ~ ATOM; State: TYPE = REF StateRecord; StateRecord: TYPE = RECORD [ op: ImagerColorOperator.ColorOperator, pa: ImagerPixelArray.PixelArray, scans, pixels: NAT _ 0, scale: REAL _ 1.0, xStart, yStart: REAL _ 0, trajectory: ImagerPath.Trajectory, dragInProgress: BOOL _ FALSE, trajectoryActive: BOOL _ FALSE, allowSubWindow: BOOL _ FALSE, prevDragX, prevDragY: REAL _ 0, xStop, yStop: REAL _ 0, clientData: REF ANY _ NIL, progressProc: ProgressProc _ NIL, rpProc: RPProc _ NIL, active: BOOL _ FALSE, drawing: BOOL _ FALSE ]; <<>> ArgSpec: TYPE = REF ArgSpecRec; ArgSpecRec: TYPE = RECORD [proc: CallBackProc]; CallBackProc: TYPE = PROC[context: Imager.Context, argSpec: ArgSpec]; <> <> CreateAISViewer: PUBLIC PROC [info: ViewerClasses.ViewerRec _ [], paint: BOOL _ TRUE] RETURNS [new: ViewerClasses.Viewer] = BEGIN IF info.name = NIL THEN info.name _ "AIS Viewer"; info.scrollable _ FALSE; new _ ViewerOps.CreateViewer[ flavor: $AISViewer, info: info]; IF paint THEN ViewerOps.PaintViewer[viewer: new, hint: all]; END; DisplayAIS: PUBLIC PROC [v: ViewerClasses.Viewer, fileName: Rope.ROPE _ NIL, wDir: ROPE _ NIL, allowSubWindow: BOOL _ FALSE, progressProc: ProgressProc _ NIL, rpProc: RPProc _ NIL, clientData: REF ANY _ NIL, paint: BOOL _ TRUE] = BEGIN fn: ROPE _ FS.ExpandName[fileName, wDir].fullFName; state: State _ NARROW [v.data]; state.clientData _ clientData; state.progressProc _ progressProc; state.rpProc _ rpProc; state.allowSubWindow _ allowSubWindow; IF fileName # NIL THEN BEGIN state.pa_ ImagerPixelArray.FromAIS[fn]; state.op_ ImagerColorOperator.GrayLinearColorModel[sWhite~255, sBlack~0]; [state.scans, state.pixels] _ GetDimensions[fn]; state.active _ TRUE; IF progressProc # NIL THEN progressProc [0, 0, state.scans, state.pixels, clientData]; IF rpProc # NIL THEN rpProc [0, 0, state.scans, state.pixels, clientData]; END ELSE state.active _ FALSE; IF paint THEN ViewerOps.PaintViewer[v, client]; END; GetCurrentSubWindow: PUBLIC PROC [v: ViewerClasses.Viewer] RETURNS [scanStart, pixelStart, scans, pixels: CARDINAL, clientData: REF ANY _ NIL] = BEGIN <> state: State _ NARROW[v.data]; IF NOT state.allowSubWindow THEN RETURN[0, 0, 0, 0, state.clientData]; [scanStart, pixelStart, scans, pixels] _ ComputeAISBox[state]; RETURN [scanStart, pixelStart, scans, pixels, state.clientData]; END; RegisterAISViewer: PUBLIC PROC [tipTable: Rope.ROPE _ NIL] = BEGIN AISViewerClass: ViewerClasses.ViewerClass; IF tipTable = NIL THEN tipTable _ "AISViewer.tip"; AISViewerClass _ NEW[ViewerClasses.ViewerClassRec _ [ paint: Paint, notify: Notify, tipTable: TIPUser.InstantiateNewTIPTable[tipTable], init: Init ]]; ViewerOps.RegisterViewerClass[$AISViewer, AISViewerClass]; END; Init: ViewerClasses.InitProc = BEGIN -- PROC [self: Viewer] ; self.data _ NEW[StateRecord]; END; Paint : ViewerClasses.PaintProc = BEGIN <> state: State _ NARROW[self.data]; state.drawing _ TRUE; IF whatChanged = NIL THEN BEGIN CallClient: PROC ~ BEGIN IF state.active THEN BEGIN viewBox: Imager.Rectangle _ ImagerBackdoor.GetBounds[context]; sx, sy: REAL _ 1.0; IF state.scans # 0 THEN sy _ (viewBox.h * 1.0) / state.scans; IF state.pixels # 0 THEN sx _ (viewBox.w * 1.0) / state.pixels; state.scale _ MIN [sx, sy]; Imager.ConcatT[context, ImagerTransformation.Scale[state.scale]]; Imager.SetSampledColor[context: context, m: ImagerTransformation.Scale[1.0], pa: state.pa, colorOperator: state.op]; Imager.MaskBox[context, [0,0, state.pixels, state.scans]]; state.trajectoryActive _ FALSE; END; END; Imager.DoSaveAll[context, CallClient]; END ELSE BEGIN CallClient: PROC ~ BEGIN argSpec: ArgSpec _ NARROW[whatChanged]; argSpec.proc[context, argSpec]; END; Imager.DoSaveAll[context, CallClient]; END; state.drawing _ FALSE; END; <<>> Notify: ViewerClasses.NotifyProc = BEGIN <> <<-- Tokens arrive from the TIP processor and are used to set up state variables and control>> <<-- feed-back. Every token list arriving from tip is expected to start with mouse coords>> SetXY: PROC = TRUSTED BEGIN TranslateMouse: CallBackProc ~ TRUSTED {x_ xy.mouseX; y_ xy.mouseY}; ViewerOps.PaintViewer[viewer: self, hint: client, clearClient: FALSE, whatChanged: NEW[ArgSpecRec _ [proc: TranslateMouse]]]; END; XorBox: PROC ~ TRUSTED BEGIN XorBoxCtxt: CallBackProc ~ TRUSTED BEGIN Imager.SetColor[context, ImagerBackdoor.invert]; Imager.MaskStrokeTrajectory[context, state.trajectory]; END; ViewerOps.PaintViewer[viewer: self, hint: client, clearClient: FALSE, whatChanged: NEW[ArgSpecRec _ [proc: XorBoxCtxt]]]; END; NotInImage: PROC RETURNS [BOOL] = BEGIN SetXY[]; IF x > (state.pixels * state.scale) THEN RETURN [TRUE]; IF y > (state.scans * state.scale) THEN RETURN [TRUE]; RETURN [FALSE]; END; state: State _ NARROW[self.data]; x, y: REAL _ 0; xy: TIPUser.TIPScreenCoords _ NARROW [input.first]; IF state.drawing THEN RETURN; IF NOT state.allowSubWindow THEN RETURN; IF NotInImage[] THEN RETURN; <<-- parse the list of command tokens following the mouse pos>> FOR input _ input.rest, input.rest WHILE input # NIL DO SELECT input.first FROM $Down => BEGIN IF state.trajectoryActive THEN XorBox[]; -- get rid of previous box state.trajectoryActive _ FALSE; SetXY[]; -- Get position at down stroke state.xStart _ x; state.yStart _ y; state.prevDragX _ x; state.prevDragY _ y; -- remember where feedback was drawn state.dragInProgress _ TRUE; IF state.progressProc # NIL THEN BEGIN scanStart, pixelStart, scans, pixels: CARDINAL; [scanStart, pixelStart, scans, pixels] _ ComputeAISBox[state]; state.progressProc[scanStart, pixelStart, scans, pixels, state.clientData]; END; IF state.rpProc # NIL THEN BEGIN scanStart, pixelStart, scans, pixels: REAL; [scanStart, pixelStart, scans, pixels] _ ComputeRealAISBox[state]; state.rpProc[scanStart, pixelStart, scans, pixels, state.clientData]; END; END; $Move, $Release => BEGIN IF NOT state.dragInProgress THEN RETURN []; IF state.trajectoryActive THEN XorBox[]; -- get rid of previous box SetXY[]; -- load x,y with user coords of mouse state.trajectory _ ImagerPath.MoveTo[[state.xStart, state.yStart]]; state.trajectory _ ImagerPath.LineTo[state.trajectory, [state.xStart, y]]; state.trajectory _ ImagerPath.LineTo[state.trajectory, [x, y]]; state.trajectory _ ImagerPath.LineTo[state.trajectory, [x, state.yStart]]; state.trajectory _ ImagerPath.LineTo[state.trajectory, [state.xStart, state.yStart]]; state.trajectoryActive _ TRUE; XorBox[]; -- draw new box state.prevDragX _ x; state.prevDragY _ y; -- remember where feedback was drawn IF state.progressProc # NIL THEN BEGIN scanStart, pixelStart, scans, pixels: CARDINAL; [scanStart, pixelStart, scans, pixels] _ ComputeAISBox[state]; state.progressProc[scanStart, pixelStart, scans, pixels, state.clientData]; END; IF state.rpProc # NIL THEN BEGIN scanStart, pixelStart, scans, pixels: REAL; [scanStart, pixelStart, scans, pixels] _ ComputeRealAISBox[state]; state.rpProc[scanStart, pixelStart, scans, pixels, state.clientData]; END; IF input.first = $Release THEN BEGIN state.xStop _ x; state.yStop _ y; state.dragInProgress _ FALSE; END; END; ENDCASE => ERROR; ENDLOOP; END; <<>> <> <<>> GetDimensions: PROC [f: Rope.ROPE] RETURNS [scans, pixels: NAT] = BEGIN <<-- return the dimensions of an AIS file>> fd: AIS.FRef _ AIS.OpenFile[name: f]; r: AIS.Raster _ AIS.ReadRaster[fd]; AIS.CloseFile[fd]; RETURN [r.scanCount, r.scanLength]; END; ComputeAISBox: PROC [state: State] RETURNS [scanStart, pixelStart, scans, pixels: CARDINAL] = BEGIN y: REAL _ (state.scans * 1.0) - (state.prevDragY/state.scale); yStart: REAL _ (state.scans * 1.0) - (state.yStart/state.scale); x: REAL _ state.prevDragX/state.scale; xStart: REAL _ state.xStart/state.scale; IF y < yStart THEN {scanStart _ Real.RoundC[y]; scans _ Real.RoundC[yStart] - scanStart} ELSE {scanStart _ Real.RoundC[yStart]; scans _ Real.RoundC[y] - scanStart}; IF x < xStart THEN {pixelStart _ Real.RoundC[x]; pixels _ Real.RoundC[xStart] - pixelStart} ELSE {pixelStart _ Real.RoundC[xStart]; pixels _ Real.RoundC[x] - pixelStart}; END; ComputeRealAISBox: PROC [state: State] RETURNS [scanStart, pixelStart, scans, pixels: REAL] = BEGIN y: REAL _ (state.scans * 1.0) - (state.prevDragY/state.scale); yStart: REAL _ (state.scans * 1.0) - (state.yStart/state.scale); x: REAL _ state.prevDragX/state.scale; xStart: REAL _ state.xStart/state.scale; IF y < yStart THEN {scanStart _ y; scans _ yStart - scanStart} ELSE {scanStart _ yStart; scans _ y - scanStart}; IF x < xStart THEN {pixelStart _ x; pixels _ xStart - pixelStart} ELSE {pixelStart _ xStart; pixels _ x - pixelStart}; END; RegisterAISViewer[]; END.