-- File: SVViewerUserImplB.mesa
-- Last edited by Bier on June 28, 1983 5:05 pm
-- Author: Eric Bier in October 1982
-- Contents: Code to respond to button presses made in an SVViewer

DIRECTORY
AIS,
 BandsClient,
 Buttons,
 CastRays,
 CGDevice,
 ColorMap,
 Containers,
 CoordSys,
CSG,
 CSGGraphics,
 DisplayList3d,
 DitherAIS,
 Draw3d,
 FileIO,
 Graphics,
 GraphicsBasic,
 GraphicsColor,
 GraphicsToPress,
IO,
 Labels,
 Map,
 Matrix3d,
 Menus,
 MessageWindow,
 Preprocess3d,
 Real,
 Rope,
 SV2d,
 SVBoundBox,
 SVEditUser,
 SVFiles,
 SVImage,
 SVTransforms,
 SVVector3d,
 SVViewerTool,
 SVViewerTools,
 SVViewerUser,
 System,
 TFO3d,
 Time,
 ViewerClasses,
 ViewerTools;

SVViewerUserImplB: CEDAR PROGRAM
IMPORTS AIS, BandsClient, Buttons, CastRays, ColorMap, CoordSys, CSGGraphics, DisplayList3d, DitherAIS, Draw3d, FileIO, Graphics, GraphicsToPress, IO, Labels, Map, Matrix3d, MessageWindow, Preprocess3d, Real, Rope, SVBoundBox, SVEditUser, SVFiles, SVImage, SVTransforms, SVViewerUser, SVViewerTools, TFO3d, Time, ViewerTools
EXPORTS GraphicsBasic, SVViewerUser =

BEGIN

Assembly: TYPE = DisplayList3d.Assembly;
BoundBox: TYPE = SVBoundBox.BoundBox;
Camera: TYPE = CSGGraphics.Camera;
Color: TYPE = GraphicsColor.Color;
CoordSysGenerator: TYPE = DisplayList3d.CoordSysGenerator;
CSGTree: TYPE = DisplayList3d.CSGTree;
DeviceObject: PUBLIC TYPE = CGDevice.Rep;
DeviceType: TYPE = BandsClient.DeviceType;
DrawStyle: TYPE = CSGGraphics.DrawStyle;
EditToolData: TYPE = SVEditUser.EditToolData;
FrameBox: TYPE = CSGGraphics.FrameBox;
MasterObject: TYPE = DisplayList3d.MasterObject;
Matrix4by4: TYPE = Matrix3d.Matrix4by4;
MouseButton: TYPE = Menus.MouseButton;
Point2d: TYPE = SV2d.Point2d;
Primitive: TYPE = CSG.Primitive;
Scene: TYPE = DisplayList3d.Scene;
Selection: TYPE = SVEditUser.Selection;
Vector: TYPE = SVVector3d.Vector;
Viewer: TYPE = ViewerClasses.Viewer;

ViewerToolData: TYPE = REF ViewerToolDataObj;
ViewerToolDataObj: TYPE = SVViewerTool.ViewerToolDataObj;

ViewerTextData: TYPE = SVViewerTool.ViewerTextData;

ViewerPictureData: TYPE = REF ViewerPictureDataObj;
ViewerPictureDataObj: TYPE = SVViewerUser.ViewerPictureDataObj;

InteractionMode: TYPE = SVViewerUser.InteractionMode; -- {select, cast};

DCProc: TYPE = SVViewerUser.DCProc;

-- GLOBAL VARIABLES

globalTable: Map.ColorTable;
globalSceneStyleCount: NAT = 3;
globalSceneStyleArray: ARRAY[1..globalSceneStyleCount] OF Rope.ROPE
 ["WireFrame", "Shaded", "Normals"];
globalBandsDeviceCount: NAT = 3;
globalBandsDeviceArray: ARRAY[1..globalBandsDeviceCount] OF Rope.ROPE
 ["Hornet", "Screen", "Platemaker"];

Press: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED {
 viewerToolData: ViewerToolData ← NARROW[clientData];
 viewerPictureData: ViewerPictureData ← NARROW[viewerToolData.viewerPicture.data];
 camera: Camera ← viewerPictureData.camera;
-- TIMING VARABLES
 startTime: System.GreenwichMeanTime;
 endTime: System.GreenwichMeanTime;
 totalTime: LONG CARDINAL;
 timeStream: IO.STREAM;
 outStream: IO.STREAM ← SVEditUser.GetOutHandle[];
 timeRope: Rope.ROPE;
-- PRESS VARIABLES
 pressName: Rope.ROPE;
 pressStream: IO.STREAM;
 pressContext: Graphics.Context;
-- START TIMING
 startTime ← Time.Current[];
-- START PRESSING
 pressName ← ViewerTools.GetContents[viewerToolData.textSection.ais];
 pressName ← Rope.Concat[SVFiles.FilenameMinusExtension[pressName], ".press"];
 pressStream ← FileIO.Open[pressName, overwrite, none];
 pressContext ← GraphicsToPress.NewContextFromStream[
        outputStream: pressStream,
        fileNameForHeaderPage: pressName,
        resolution: 384];
 CSGGraphics.SetQualityCamera[camera, quality]; -- make a high quality image
 CSGGraphics.ColorFilmCamera[camera, FALSE]; -- but only black and white
 SVViewerUser.DrawSceneEtc[pressContext, viewerToolData];
 CSGGraphics.ColorFilmCamera[camera, TRUE]; -- restore to color mode
 CSGGraphics.SetQualityCamera[camera, fast]; -- restore to fast mode for interactive editting
 GraphicsToPress.Close[pressContext];
 endTime ← Time.Current[];
 totalTime ← endTime - startTime;
 timeStream ← IO.CreateOutputStreamToRope[];
 timeStream.PutF[" Done. Pressing took (%r)",[cardinal[totalTime]]];
 outStream.PutF[" Done. Pressing took (%r)",[cardinal[totalTime]]];
 timeRope ← timeStream.GetOutputStreamRope[];
 MessageWindow.Append[timeRope, TRUE];
 };

PressAIS: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED {
 viewerToolData: ViewerToolData ← NARROW[clientData];
 viewerPictureData: ViewerPictureData ← NARROW[viewerToolData.viewerPicture.data];
 scene: Scene ← viewerPictureData.scene;
 camera: Camera ← viewerPictureData.camera;
 minX, minY, maxX, maxY: REAL;
 tree: CSGTree;
 aisRope: Rope.ROPE ← ViewerTools.GetContents[viewerToolData.textSection.ais];
-- TIMING VARABLES
 startTime: System.GreenwichMeanTime;
 endTime: System.GreenwichMeanTime;
 totalTime: LONG CARDINAL;
 timeStream: IO.STREAM;
 timeRope: Rope.ROPE;
-- PRESS VARIABLES
 pressName: Rope.ROPE;
 pressStream: IO.STREAM;
 outStream: IO.STREAM ← SVEditUser.GetOutHandle[];
 pressContext: Graphics.Context;
-- START TIMING
 startTime ← Time.Current[];
-- START PRESSING
 pressName ← ViewerTools.GetContents[viewerToolData.textSection.ais];
 pressName ← Rope.Concat[SVFiles.FilenameMinusExtension[pressName], ".press"];
 pressStream ← FileIO.Open[pressName, overwrite, none];
 pressContext ← GraphicsToPress.NewContextFromStream[
        outputStream: pressStream,
        fileNameForHeaderPage: pressName,
        resolution: 384];
 tree ← DisplayList3d.SceneToTree[scene, camera];
 [minX, minY, maxX, maxY] ← MinAndMaxFromCameraAndTree[camera, tree];
 SVImage.DrawAndScaleColorImage[pressContext, aisRope, [camera.screenCS.mat[1][4], camera.screenCS.mat[2][4]], minX, minY, maxX, maxY];
 GraphicsToPress.Close[pressContext];
 endTime ← Time.Current[];
 totalTime ← endTime - startTime;
 timeStream ← IO.CreateOutputStreamToRope[];
 timeStream.PutF[" Done. Pressing AIS took (%r)",[cardinal[totalTime]]];
 outStream.PutF[" Done. Pressing AIS took (%r)",[cardinal[totalTime]]];
 timeRope ← timeStream.GetOutputStreamRope[];
 MessageWindow.Append[timeRope, TRUE];
 };

PressBWAIS: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED {
 viewerToolData: ViewerToolData ← NARROW[clientData];
 viewerPictureData: ViewerPictureData ← NARROW[viewerToolData.viewerPicture.data];
 editToolData: EditToolData ← NARROW[viewerToolData.editToolData];
 scene: Scene ← viewerPictureData.scene;
 camera: Camera ← viewerPictureData.camera;
 minX, minY, maxX, maxY: REAL;
 tree: CSGTree;
 aisRope: Rope.ROPE ← ViewerTools.GetContents[viewerToolData.textSection.ais];
-- TIMING VARABLES
 startTime: System.GreenwichMeanTime;
 endTime: System.GreenwichMeanTime;
 totalTime: LONG CARDINAL;
 timeStream: IO.STREAM;
 timeRope: Rope.ROPE;
-- PRESS VARIABLES
 pressName: Rope.ROPE;
 pressStream: IO.STREAM;
 outStream: IO.STREAM ← SVEditUser.GetOutHandle[];
 pressContext: Graphics.Context;
-- START TIMING
 startTime ← Time.Current[];
-- START PRESSING
 pressName ← ViewerTools.GetContents[viewerToolData.textSection.ais];
 pressName ← Rope.Concat[SVFiles.FilenameMinusExtension[pressName], ".press"];
 pressStream ← FileIO.Open[pressName, overwrite, none];
 pressContext ← GraphicsToPress.NewContextFromStream[
        outputStream: pressStream,
        fileNameForHeaderPage: pressName,
        resolution: 384];
 tree ← DisplayList3d.SceneToTree[scene, camera];
 [minX, minY, maxX, maxY] ← MinAndMaxFromCameraAndTree[camera, tree];
 SVImage.DrawAndScaleBlackAndWhiteImage[pressContext, aisRope, [camera.screenCS.mat[1][4], camera.screenCS.mat[2][4]], minX, minY, maxX, maxY];
 GraphicsToPress.Close[pressContext];
 endTime ← Time.Current[];
 totalTime ← endTime - startTime;
 timeStream ← IO.CreateOutputStreamToRope[];
 timeStream.PutF[" Done. Pressing AIS took (%r)",[cardinal[totalTime]]];
 outStream.PutF[" Done. Pressing AIS took (%r)",[cardinal[totalTime]]];
 timeRope ← timeStream.GetOutputStreamRope[];
 MessageWindow.Append[timeRope, TRUE];
 };

CrossHairs: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED {
-- viewer is the crossHairs button
 viewerToolData: ViewerToolData ← NARROW[clientData];
 viewerPictureData: ViewerPictureData ← NARROW[viewerToolData.viewerPicture.data];
 camera: Camera ← viewerPictureData.camera;
DoCrossHairs: PROC [dc: Graphics.Context] = TRUSTED {
  Draw3d.Draw2dCoordSys[dc, [0,0], camera];
  };
 SVViewerUser.Painter[DoCrossHairs, viewerToolData];
 }; -- end of CrossHairs

DrawDither: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED {
-- parent is the DrawDither button
 viewerToolData: ViewerToolData ← NARROW[clientData];
 viewerPictureData: ViewerPictureData ← NARROW[viewerToolData.viewerPicture.data];
 scene: Scene ← viewerPictureData.scene;
 minX, minY, maxX, maxY: REAL;
 camera: Camera ← viewerPictureData.camera;
 tree: CSGTree;
 aisRope: Rope.ROPE ← ViewerTools.GetContents[viewerToolData.textSection.ais];
DoDrawBlackAndWhite: PROC [dc: Graphics.Context] = TRUSTED {
  SVImage.DrawAndScaleBlackAndWhiteImage[dc, aisRope, [camera.screenCS.mat[1][4], camera.screenCS.mat[2][4]], minX, minY, maxX, maxY];
  };
 aisRope ← Rope.Concat[SVFiles.FilenameMinusExtension[aisRope], "-std.ais"];
 tree ← DisplayList3d.SceneToTree[scene, camera];
 [minX, minY, maxX, maxY] ← MinAndMaxFromCameraAndTree[camera, tree];
SVViewerUser.Painter[DoDrawBlackAndWhite, viewerToolData];
 }; -- end of DrawDither


Dither: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED {
 viewerToolData: ViewerToolData ← NARROW[clientData];
 baseName, redName, greenName, blueName, FSFilename: Rope.ROPE;
 baseName ← ViewerTools.GetContents[viewerToolData.textSection.ais];
 baseName ← SVFiles.FilenameMinusExtension[baseName];
 redName ← Rope.Concat[baseName, "-red.ais"];
 greenName ← Rope.Concat[baseName, "-green.ais"];
 blueName ← Rope.Concat[baseName, "-blue.ais"];
 FSFilename ← Rope.Concat[baseName, "-std.ais"];
BEGIN
 globalTable ← Preprocess3d.GetGlobalTable[];
  DitherAIS.Squash[redName, greenName, blueName, FSFilename, MyMap
   ! AIS.Error => {
    outStream: IO.STREAM ← SVEditUser.GetOutHandle[];
    outStream.PutF["%g -*.ais - Bad file names", [rope[baseName]]];
    MessageWindow.Append[Rope.Concat[baseName, "-*.ais - Bad file names"], TRUE];
    GOTO giveUp}];
  EXITS
   giveUp => NULL;
END;
 };

MyMap: DitherAIS.UserMapProc = TRUSTED {
 rb, gb, bb: Map.ColorMapSize;
 palix ← Map.GetIndex[r, g, b, globalTable];
 [rb, gb, bb] ← ColorMap.GetColor[palix]; -- ought to use the table
 re ← r - rb;
 ge ← g - gb;
 be ← b - bb;
 };

Extend: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED {
-- Get the edittool to extend the current secondary selection.
 SVEditUser.ExtendSecondary[];
 };

Move: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED {
 newMat: Matrix4by4;
 primary, secondary: Selection;
 primary ← SVEditUser.GetPrimarySelection[];
 secondary ← SVEditUser.GetSecondarySelection[];
IF primary = NIL THEN RETURN[];
IF primary.assembly = NIL THEN RETURN;
IF secondary = NIL THEN RETURN[];
IF secondary.assembly = NIL THEN RETURN;
-- If the two selections are in different viewers, then see if these viewers refer to different scenes. If so do a scene to scene copy before doing any aligning and abutting
IF primary.viewerToolData # secondary.viewerToolData THEN {
  primaryPicture: ViewerPictureData ←
   NARROW[primary.viewerToolData.viewerPicture.data];
  secondaryPicture: ViewerPictureData ←
   NARROW[secondary.viewerToolData.viewerPicture.data];
  IF primaryPicture.scene # secondaryPicture.scene THEN {
   copyAssembly: Assembly ← DisplayList3d.MergeAssemblyIntoScene[secondary.assembly, secondaryPicture.scene, primaryPicture.scene.assembly, primaryPicture.scene];
   -- The new assembly has the same relationship to scene assembly as the last assembly had to its parent. This will do for now.
   SVViewerUser.SceneNewVersion[primary.viewerToolData];
   SVViewerUser.DrawSceneInternal[NIL, primary.viewerToolData, red, FALSE, FALSE];
   SVViewerUser.DrawSceneInternal[NIL, secondary.viewerToolData, red, FALSE, FALSE];
   RETURN;
   };
  };
 newMat ← Matrix3d.MakeRotateYMat[180];
 SVTransforms.PlaceIndirect[secondary.assembly, secondary.coordSys, primary.coordSys, newMat];
  -- Rotate them so they are tangent in an exterior fashion
 SVViewerUser.SceneNewVersion[primary.viewerToolData];
 SVViewerUser.DrawSceneInternal[NIL, primary.viewerToolData, red, FALSE, FALSE];
 };

DrawPt: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED {
 viewerToolData: ViewerToolData ← NARROW[clientData];
 viewerPictureData: ViewerPictureData ← NARROW[viewerToolData.viewerPicture.data];
 camera: Camera ← viewerPictureData.camera;
-- get x and y from appropriate slots. Interpret this as camera coordinates and draw a cross.
 x, y: REAL;
DoDrawPt: PROC [dc: Graphics.Context] = TRUSTED {
  Draw3d.DrawX[dc, [x, y], camera];
  };
 [x, y] ← SVViewerUser.ReadTwoReals[viewerToolData.textSection.xyz];
 SVViewerUser.Painter[DoDrawPt, viewerToolData];
 };

MinAndMaxFromCameraAndTree: PRIVATE PROC [camera: Camera, tree: CSGTree] RETURNS [minX, minY, maxX, maxY: REAL] = TRUSTED {
 frame: FrameBox ← camera.frame;
 boundBox: BoundBox;
IF frame.fullScreen THEN {
  boundBox ← Preprocess3d.Preprocess[tree, camera];
  minX ← boundBox.minVert[1]; minY ← boundBox.minVert[2];
  maxX ← boundBox.maxVert[1]; maxY ← boundBox.maxVert[2];
  }
ELSE {
  minX ← frame.downLeft[1]; minY ← frame.downLeft[2];
  maxX ← frame.upRight[1]; maxY ← frame.upRight[2];
  };
 };

DrawColor: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED {
-- parent is the DrawColor button
 viewerToolData: ViewerToolData ← NARROW[clientData];
 viewerPictureData: ViewerPictureData ← NARROW[viewerToolData.viewerPicture.data];
 editToolData: EditToolData ← NARROW[viewerToolData.editToolData];
 scene: Scene ← viewerPictureData.scene;
 resolution: REAL ← SVViewerTools.GetReal[editToolData.cameraSection.resolution, 72.0];
 boundBox: BoundBox;
 minX, minY, maxX, maxY: REAL;
 camera: Camera ← viewerPictureData.camera;
 tree: CSGTree;
 aisRope: Rope.ROPE ← ViewerTools.GetContents[viewerToolData.textSection.ais];
DoDrawColor: PROC [dc: Graphics.Context] = TRUSTED {
  SVImage.DrawAlignedColorImage[dc, aisRope, resolution, [camera.screenCS.mat[1][4], camera.screenCS.mat[2][4]], boundBox];
  };
 tree ← DisplayList3d.SceneToTree[scene, camera];
 [minX, minY, maxX, maxY] ← MinAndMaxFromCameraAndTree[camera, tree];
 boundBox ← SVBoundBox.BoundBoxFromValues[minX, minY, maxX, maxY];
SVViewerUser.Painter[DoDrawColor, viewerToolData];
 }; -- end of DrawColor

DrawBlackAndWhite: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED {
-- parent is the DrawB&W button
 viewerToolData: ViewerToolData ← NARROW[clientData];
 viewerPictureData: ViewerPictureData ← NARROW[viewerToolData.viewerPicture.data];
 editToolData: EditToolData ← NARROW[viewerToolData.editToolData];
 resolution: REAL ← SVViewerTools.GetReal[editToolData.cameraSection.resolution, 72.0];
 scene: Scene ← viewerPictureData.scene;
 boundBox: BoundBox;
 minX, minY, maxX, maxY: REAL;
 camera: Camera ← viewerPictureData.camera;
 tree: CSGTree;
 aisRope: Rope.ROPE ← ViewerTools.GetContents[viewerToolData.textSection.ais];
DoDrawBlackAndWhite: PROC [dc: Graphics.Context] = TRUSTED {
  SVImage.DrawAlignedBlackAndWhiteImage[dc, aisRope, resolution, [camera.screenCS.mat[1][4], camera.screenCS.mat[2][4]], boundBox, FALSE];
  };
 tree ← DisplayList3d.SceneToTree[scene, camera];
 [minX, minY, maxX, maxY] ← MinAndMaxFromCameraAndTree[camera, tree];
 boundBox ← SVBoundBox.BoundBoxFromValues[minX, minY, maxX, maxY];
SVViewerUser.Painter[DoDrawBlackAndWhite, viewerToolData];
 }; -- end of DrawBlackAndWhite

SetFocalLength: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED {
 viewerToolData: ViewerToolData ← NARROW[clientData];
 viewerPictureData: ViewerPictureData ← NARROW[viewerToolData.viewerPicture.data];
 viewerPictureData.camera.focalLength ← SVViewerTools.GetReal[viewerToolData.textSection.focalLength, 1800];
 }; -- end of SetFocalLength

MakeBands: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED {
 viewerToolData: ViewerToolData ← NARROW[clientData];
 viewerPictureData: ViewerPictureData ← NARROW[viewerToolData.viewerPicture.data];
 camera: Camera ← viewerPictureData.camera;
 deviceType: DeviceType;
 deviceName: Rope.ROPE;
-- TIMING VARABLES
 startTime: System.GreenwichMeanTime;
 endTime: System.GreenwichMeanTime;
 totalTime: LONG CARDINAL;
 timeStream: IO.STREAM;
 outStream: IO.STREAM ← SVEditUser.GetOutHandle[];
 timeRope: Rope.ROPE;
-- BANDS VARIABLES
 bandContext: Graphics.Context;
 bandDevice: BandsClient.Device;
-- START TIMING
 startTime ← Time.Current[];
-- START BANDS
 deviceName ← globalBandsDeviceArray[viewerPictureData.bandsDeviceIndex];
SELECT TRUE FROM
  Rope.Equal[deviceName, "Hornet"] => deviceType ← hornet;
  Rope.Equal[deviceName, "Screen"] => deviceType ← screen;
  Rope.Equal[deviceName, "Platemaker"] => deviceType ← platemaker;
ENDCASE => ERROR;
BandsClient.SetUpSeparation[none, Tentfilter];
 [bandContext, bandDevice] ← BandsClient.OpenBands[deviceType];
 CSGGraphics.SetQualityCamera[camera, quality]; -- make a high quality image
 CSGGraphics.ColorFilmCamera[camera, FALSE]; -- but only black and white

 SVViewerUser.DrawSceneEtc[bandContext, viewerToolData];

 CSGGraphics.ColorFilmCamera[camera, TRUE]; -- restore to color mode
 CSGGraphics.SetQualityCamera[camera, fast]; -- restore to fast mode for interactive editting
 BandsClient.CloseBands[bandDevice, deviceType];

 endTime ← Time.Current[];
 totalTime ← endTime - startTime;
 timeStream ← IO.CreateOutputStreamToRope[];
 timeStream.PutF[" Done. Bands took (%r)",[cardinal[totalTime]]];
 outStream.PutF[" Done. Bands took (%r)",[cardinal[totalTime]]];
 timeRope ← timeStream.GetOutputStreamRope[];
 MessageWindow.Append[timeRope, TRUE];
 };

Tentfilter: PROC [x,y: REAL] RETURNS [val: REAL] = TRUSTED {
RETURN [1.0-ABS[x]];
};

-- PROMPTS

AISPrompt: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED {
 viewerToolData: ViewerToolData ← NARROW[clientData];
 ViewerTools.SetSelection[viewerToolData.textSection.ais];
 };

XYZPrompt: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED {
 viewerToolData: ViewerToolData ← NARROW[clientData];
 ViewerTools.SetSelection[viewerToolData.textSection.xyz];
 };

FocalLengthPrompt: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED {
 viewerToolData: ViewerToolData ← NARROW[clientData];
 ViewerTools.SetSelection[viewerToolData.textSection.focalLength];
 };

PictureFilePrompt: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED {
 viewerToolData: ViewerToolData ← NARROW[clientData];
 ViewerTools.SetSelection[viewerToolData.textSection.fileName];
 };

-- PROMPTS WHICH CYCLE THROUGH POSSIBILITIES

StylePrompt: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED {
 viewerToolData: ViewerToolData ← NARROW[clientData];
 viewerPictureData: ViewerPictureData ← NARROW[viewerToolData.viewerPicture.data];
 newStyle: DrawStyle;
 scene: Scene ← viewerPictureData.scene;
 viewerPictureData.sceneStyleIndex ← IF viewerPictureData.sceneStyleIndex = globalSceneStyleCount THEN 1 ELSE viewerPictureData.sceneStyleIndex + 1;
 Labels.Set[viewerToolData.textSection.viewStyle, globalSceneStyleArray[viewerPictureData.sceneStyleIndex]];
SELECT globalSceneStyleArray[viewerPictureData.sceneStyleIndex] FROM
  "WireFrame" => newStyle ← wire;
  "Shaded" => newStyle ← shaded;
  "Normals" => newStyle ← normals;
  -- "RayCast" => newStyle ← rayCast;
  ENDCASE => ERROR;
 viewerPictureData.camera.style ← newStyle;
 };

-- ON/OFF SWITCHES

DoubleBuffer: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED {
 button: Buttons.Button ← NARROW[parent];
 viewerToolData: ViewerToolData ← NARROW[clientData];
 viewerPictureData: ViewerPictureData ← NARROW[viewerToolData.viewerPicture.data];
IF viewerPictureData.doubleBuffer THEN {
  viewerPictureData.doubleBuffer ← NOT viewerPictureData.doubleBuffer;
  Buttons.SetDisplayStyle[button, $BlackOnWhite];
  }
ELSE {viewerPictureData.doubleBuffer ← NOT viewerPictureData.doubleBuffer;
  Buttons.SetDisplayStyle[button, $WhiteOnBlack];
  };
 }; -- end of DoubleBuffer

ShowCoordsMode: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED {
 button: Buttons.Button ← NARROW[parent];
 viewerToolData: ViewerToolData ← NARROW[clientData];
 viewerPictureData: ViewerPictureData ← NARROW[viewerToolData.viewerPicture.data];
IF viewerPictureData.showCoordSys THEN {
  viewerPictureData.showCoordSys ← NOT viewerPictureData.showCoordSys;
  Buttons.SetDisplayStyle[button, $BlackOnWhite];
  }
ELSE {viewerPictureData.showCoordSys ← NOT viewerPictureData.showCoordSys;
  Buttons.SetDisplayStyle[button, $WhiteOnBlack];
  };
 }; -- end of ShowCoordsMode

-- OPTIONS LISTERS

BandsDevice: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED {
 viewerToolData: ViewerToolData ← NARROW[clientData];
 thisButton: Viewer ← NARROW[parent];
 viewerPictureData: ViewerPictureData ← NARROW[viewerToolData.viewerPicture.data];
 scene: Scene ← viewerPictureData.scene;
 viewerPictureData.bandsDeviceIndex ← IF viewerPictureData.bandsDeviceIndex = globalBandsDeviceCount THEN 1 ELSE viewerPictureData.bandsDeviceIndex + 1;
 Buttons.ReLabel[thisButton, globalBandsDeviceArray[viewerPictureData.bandsDeviceIndex]];
 };

-- UTILITIES

Selected: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED {
-- this window has been selected. Show its Selected button as WhiteOnBlack.
 viewerToolData: ViewerToolData ← NARROW[clientData];
 viewerToolData.textSection.isSelected ← TRUE;
 Labels.SetDisplayStyle[viewerToolData.textSection.selected, $WhiteOnBlack, TRUE];
 SVEditUser.NewSelectedViewer[viewerToolData];
 };

Deselected: PUBLIC PROC [viewerToolData: ViewerToolData] = TRUSTED {
-- this window has been deselected. Show its Selected button as BlackOnWhite.
IF viewerToolData = NIL THEN RETURN;
 viewerToolData.textSection.isSelected ← FALSE;
 Labels.SetDisplayStyle[viewerToolData.textSection.selected, $BlackOnWhite, TRUE];
 };

-- Input Notify Procs

SingleRay: PUBLIC PROC [viewerToolData: ViewerToolData, cameraPoint: Point2d] = TRUSTED {
 viewerPictureData: ViewerPictureData ← NARROW[viewerToolData.viewerPicture.data];
 f: IO.STREAM ← SVEditUser.GetOutHandle[];
 color: Color;
 xInt, yInt: INTEGER;
 tree: CSGTree;
 scene: Scene ← viewerPictureData.scene;
 camera: Camera ← viewerPictureData.camera;
 xInt ← Real.FixI[cameraPoint[1]]; yInt ← Real.FixI[cameraPoint[2]];
 tree ← DisplayList3d.SceneToTree[scene, camera];
 color ← CastRays.SingleRay[xInt, yInt, tree, scene.lightSources, camera, TRUE, f];
 f.PutF["[%g,%g]: ",[real[cameraPoint[1]]], [real[cameraPoint[2]]]];
 TFO3d.FileoutColor[f, color];
 };

PointAt: PUBLIC PROC [viewerToolData: ViewerToolData, cameraPoint: Point2d] = TRUSTED {
-- adds a new assembly to the scene of class "CoordSys", isTool is true. The first one will be  pointer.0, the second pointer.1 and so on. Pointer will be defined with respect to the parent of the assembly which we are pointing at. If we are not pointing at anything, don't create a pointer. Just return.
 viewerPictureData: ViewerPictureData ← NARROW[viewerToolData.viewerPicture.data];
 f: IO.STREAM ← SVEditUser.GetOutHandle[];
 tree: CSGTree;
 normal: Vector;
 prim: Primitive;
 scene: Scene ← viewerPictureData.scene;
 camera: Camera ← viewerPictureData.camera;
 class: CSG.Classification;
 tree ← DisplayList3d.SceneToTree[scene, camera];
 class ← CastRays.SingleRay2[cameraPoint, tree, scene.lightSources, camera, TRUE, f];
IF class.count = 0 THEN RETURN;
 prim ← class.primitives[1];
 normal ← class.normals[1];
 };

RayCastProgress: PUBLIC CastRays.NotifyOfProgressProc = TRUSTED {
-- PROC [currentY, minX, minY, maxX, maxY: REAL, clientData: REF ANY];
-- Draw a box of width 10 and length currentY-minY+1 with origin at (minX, minY)
 viewerToolData: ViewerToolData ← NARROW[clientData];
 viewerPictureData: ViewerPictureData ← NARROW[viewerToolData.viewerPicture.data];
 camera: Camera ← viewerPictureData.camera;
 width: REAL ← 10;
  box: Graphics.Box;
  lowLeft, upRight: Point2d;
DoRayCastProgress: PROC [dc: Graphics.Context] = TRUSTED {
   Graphics.SetColor[dc, GraphicsColor.black];
   Graphics.DrawBox[dc, box];
  };
  lowLeft ← CoordSys.CameraToScreen[[minX, minY], camera.screenCS];
  upRight ← CoordSys.CameraToScreen[[maxX, currentY], camera.screenCS];
  box ← [lowLeft[1], lowLeft[2], upRight[1], upRight[2]];
  SVViewerUser.Painter[DoRayCastProgress, viewerToolData];
  };

END.