<> <> <> <> <> DIRECTORY AIS, BasicTime, Buttons, CastRays, CGDevice, ColorMap, Containers, CoordSys, CSG, CSGGraphics, DisplayList3d, DisplayListToTree, DisplayMap, DitherAIS, FS, Graphics, GraphicsBasic, GraphicsColor, GraphicsToPress, InputFocus, IO, Labels, Menus, MessageWindow, Preprocess3d, Real, Rope, SV2d, SV3d, SVBoundBox, SVDraw3d, SVEditUser, SVError, SVFileoutPoly, SVFiles, SVImage, SVInterfaceTypes, SVModelTypes, SVRayTypes, SVSceneTypes, SVSelections, SVSlices, SVTransforms, SVViewerTool, SVViewerTools, SVViewerUser, TFO3d, ViewerClasses, ViewerTools; SVViewerUserImplB: CEDAR PROGRAM IMPORTS AIS, BasicTime, Buttons, CastRays, ColorMap, CoordSys, CSG, CSGGraphics, DisplayList3d, DisplayListToTree, DisplayMap, DitherAIS, FS, Graphics, GraphicsToPress, InputFocus, IO, Labels, MessageWindow, Preprocess3d, Real, Rope, SVBoundBox, SVDraw3d, SVEditUser, SVError, SVFiles, SVFileoutPoly, SVImage, SVSelections, SVSlices, SVViewerUser, SVViewerTools, TFO3d, ViewerTools EXPORTS GraphicsBasic, SVViewerUser = BEGIN Assembly: TYPE = SVSceneTypes.Assembly; BoundBox: TYPE = SVModelTypes.BoundBox; Camera: TYPE = SVModelTypes.Camera; Classification: TYPE = SVRayTypes.Classification; Color: TYPE = GraphicsColor.Color; CoordSysGenerator: TYPE = DisplayList3d.CoordSysGenerator; CSGTree: TYPE = SVRayTypes.CSGTree; DeviceObject: PUBLIC TYPE = CGDevice.Rep; DrawStyle: TYPE = SVModelTypes.DrawStyle; EditToolData: TYPE = SVEditUser.EditToolData; FrameBox: TYPE = SVModelTypes.FrameBox; MasterObject: TYPE = SVSceneTypes.MasterObject; Matrix4by4: TYPE = SV3d.Matrix4by4; MouseButton: TYPE = Menus.MouseButton; Point2d: TYPE = SV2d.Point2d; Primitive: TYPE = SVRayTypes.Primitive; Ray: TYPE = SVRayTypes.Ray; Scene: TYPE = SVSceneTypes.Scene; Selection: TYPE = SVInterfaceTypes.Selection; SelectionType: TYPE = SVInterfaceTypes.SelectionType; Slice: TYPE = SVSlices.Slice; Vector: TYPE = SV3d.Vector; Viewer: TYPE = ViewerClasses.Viewer; ViewerToolData: TYPE = SVInterfaceTypes.ViewerToolData; DCProc: TYPE = SVInterfaceTypes.DCProc; <> globalTable: DisplayMap.ColorTable; globalSceneStyleCount: NAT = 3; globalSceneStyleArray: ARRAY[1..globalSceneStyleCount] OF Rope.ROPE _ ["WireFrame", "Shaded", "Normals"]; Press: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED { viewerToolData: ViewerToolData _ NARROW[clientData]; camera: Camera _ viewerToolData.camera; <> startTime: BasicTime.GMT; endTime: BasicTime.GMT; totalTime: INT; timeRope: Rope.ROPE; <> pressName: Rope.ROPE; pressContext: Graphics.Context; <> pressName _ ViewerTools.GetContents[viewerToolData.textSection.ais]; pressName _ Rope.Concat[SVFiles.FilenameMinusExtension[pressName], ".press"]; SVError.Append[Rope.Cat["Pressing ", pressName, "... "], TRUE]; startTime _ BasicTime.Now[]; <> pressContext _ GraphicsToPress.NewContext[pressName]; CSGGraphics.Clip[pressContext, camera]; CSGGraphics.SetQualityCamera[camera, quality]; -- make a high quality image CSGGraphics.ColorFilmCamera[camera, FALSE]; -- but only black and white SVViewerUser.DrawSceneEtc[pressContext, viewerToolData, TRUE]; CSGGraphics.ColorFilmCamera[camera, TRUE]; -- restore to color mode CSGGraphics.SetQualityCamera[camera, fast]; -- restore to fast mode for interactive editting GraphicsToPress.Close[pressContext]; <> endTime _ BasicTime.Now[]; totalTime _ BasicTime.Period[startTime, endTime]; timeRope _ IO.PutFR[" Done making %g. Pressing took (%r)", [rope[pressName]], [integer[totalTime]]]; SVError.Append[timeRope, FALSE, TRUE]; }; PressAIS: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED { viewerToolData: ViewerToolData _ NARROW[clientData]; scene: Scene _ viewerToolData.scene; camera: Camera _ viewerToolData.camera; minX, minY, maxX, maxY: REAL; tree: CSGTree; aisRope: Rope.ROPE _ ViewerTools.GetContents[viewerToolData.textSection.ais]; <> startTime: BasicTime.GMT; endTime: BasicTime.GMT; totalTime: INT; timeRope: Rope.ROPE; <> pressName: Rope.ROPE; pressStream: IO.STREAM; pressContext: Graphics.Context; <> startTime _ BasicTime.Now[]; <> pressName _ ViewerTools.GetContents[viewerToolData.textSection.ais]; pressName _ Rope.Concat[SVFiles.FilenameMinusExtension[pressName], ".press"]; pressStream _ FS.StreamOpen[pressName, $create]; pressContext _ GraphicsToPress.NewContextFromStream[ outputStream: pressStream, fileNameForHeaderPage: pressName, resolution: 384]; tree _ DisplayListToTree.AssemblyToTree[scene.assembly, 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 _ BasicTime.Now[]; totalTime _ BasicTime.Period[startTime, endTime]; timeRope _ IO.PutFR[" Done. Pressing AIS took (%r)",[integer[totalTime]]]; SVError.Append[timeRope, TRUE, TRUE]; }; <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <<[minX, minY, maxX, maxY] _ MinAndMaxFromCameraAndTree[camera, tree];>> <> <> <> <> <> <> <<};>> StorePoly: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED { viewerToolData: ViewerToolData _ NARROW[clientData]; editToolData: EditToolData _ NARROW[viewerToolData.editToolData]; wdir: Rope.ROPE; success, confirmation: BOOL; f: IO.STREAM; picName: Rope.ROPE _ ViewerTools.GetSelectionContents[]; fullName: Rope.ROPE; scene: Scene _ viewerToolData.scene; IF Rope.Length[picName] = 0 THEN RETURN; wdir _ editToolData.originalWorkingDirectory; success _ TRUE; [fullName,,] _ FS.ExpandName[picName, wdir ! FS.Error => IF error.group = user THEN { success _ FALSE; CONTINUE; } ]; IF NOT success THEN RETURN; IF SVFiles.FileExists[fullName] THEN { confirmation _ MessageWindow.Confirm["Confirm overwrite of existing pic file"]; IF confirmation THEN f _ FS.StreamOpen[fullName, $create] ELSE RETURN; } ELSE f _ FS.StreamOpen[fullName, $create]; SVError.Append["Writing scene: ",TRUE]; SVError.Append[fullName]; SVError.Append[" to file: "]; SVError.Append[fullName]; SVError.Append["..."]; IF NOT success THEN RETURN; SVFileoutPoly.StorePolyScene[scene, f, fullName]; SVError.Append["Done"]; }; -- end of StorePoly CrossHairs: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED { <> viewerToolData: ViewerToolData _ NARROW[clientData]; camera: Camera _ viewerToolData.camera; DoCrossHairs: PROC [dc: Graphics.Context] = TRUSTED { SVDraw3d.Draw2dCoordSys[dc, [0,0], camera]; }; SVViewerUser.Painter[DoCrossHairs, viewerToolData]; }; -- end of CrossHairs 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, "-grn.ais"]; blueName _ Rope.Concat[baseName, "-blu.ais"]; FSFilename _ Rope.Concat[baseName, "-std.ais"]; BEGIN globalTable _ Preprocess3d.GetGlobalTable[]; DitherAIS.Squash[redName, greenName, blueName, FSFilename, MyMap ! AIS.Error => TRUSTED { msgRope: Rope.ROPE; msgRope _ IO.PutFR["%g -*.ais - Bad file names", [rope[baseName]]]; SVError.Append[Rope.Concat[baseName, "-*.ais - Bad file names"], TRUE, TRUE]; GOTO giveUp}]; <> DrawDither[parent, clientData, mouseButton, shift, control]; EXITS giveUp => NULL; END; }; MyMap: DitherAIS.UserMapProc = TRUSTED { rb, gb, bb: DisplayMap.ColorMapSize; palix _ DisplayMap.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; }; <<>> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <<};>> <<};>> <> <> <> <> <> <<};>> DrawPt: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED { viewerToolData: ViewerToolData _ NARROW[clientData]; camera: Camera _ viewerToolData.camera; <> x, y: REAL; DoDrawPt: PROC [dc: Graphics.Context] = TRUSTED { SVDraw3d.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; defaultHalfSide: REAL = 100.0; IF frame.fullScreen THEN { [boundBox] _ Preprocess3d.PreprocessForInteraction[tree, camera]; IF boundBox = NIL THEN { minX _ minY _ -defaultHalfSide; maxX _ maxY _ defaultHalfSide; } ELSE { 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 { <> viewerToolData: ViewerToolData _ NARROW[clientData]; scene: Scene _ viewerToolData.scene; resolution: REAL; boundBox: BoundBox; minX, minY, maxX, maxY: REAL; camera: Camera _ viewerToolData.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]; }; resolution _ camera.resolution; tree _ DisplayListToTree.AssemblyToTree[scene.assembly, 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 { <> viewerToolData: ViewerToolData _ NARROW[clientData]; resolution: REAL; scene: Scene _ viewerToolData.scene; boundBox: BoundBox; minX, minY, maxX, maxY: REAL; camera: Camera _ viewerToolData.camera; tree: CSGTree; oldMode: Graphics.PaintMode; 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]; }; DoDrawFrame: SAFE PROC [dc: Graphics.Context] = TRUSTED { Graphics.SetColor[dc, GraphicsColor.black]; oldMode _ Graphics.SetPaintMode[dc, invert]; CSGGraphics.DrawFrame [dc, camera]; [] _ Graphics.SetPaintMode[dc, oldMode]; }; resolution _ camera.resolution; tree _ DisplayListToTree.AssemblyToTree[scene.assembly, scene, camera]; [minX, minY, maxX, maxY] _ MinAndMaxFromCameraAndTree[camera, tree]; boundBox _ SVBoundBox.BoundBoxFromValues[minX, minY, maxX, maxY]; SVViewerUser.Painter[DoDrawBlackAndWhite, viewerToolData]; SVViewerUser.Painter[DoDrawFrame, viewerToolData]; -- redraw frame }; -- end of DrawBlackAndWhite DrawDither: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED { <> viewerToolData: ViewerToolData _ NARROW[clientData]; resolution: REAL; scene: Scene _ viewerToolData.scene; boundBox: BoundBox; minX, minY, maxX, maxY: REAL; camera: Camera _ viewerToolData.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, TRUE]; }; resolution _ camera.resolution; aisRope _ Rope.Concat[SVFiles.FilenameMinusExtension[aisRope], "-std.ais"]; tree _ DisplayListToTree.AssemblyToTree[scene.assembly, scene, camera]; [minX, minY, maxX, maxY] _ MinAndMaxFromCameraAndTree[camera, tree]; boundBox _ SVBoundBox.BoundBoxFromValues[minX, minY, maxX, maxY]; SVViewerUser.Painter[DoDrawBlackAndWhite, viewerToolData]; }; -- end of DrawDither SetFocalLength: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED { viewerToolData: ViewerToolData _ NARROW[clientData]; viewerToolData.camera.focalLength _ SVViewerTools.GetReal[viewerToolData.textSection.focalLength, 1800]; }; -- end of SetFocalLength <> 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]; }; TextPrompt: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED { viewerToolData: ViewerToolData _ NARROW[clientData]; ViewerTools.SetSelection[viewerToolData.textSection.text]; }; <> StylePrompt: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED { viewerToolData: ViewerToolData _ NARROW[clientData]; newStyle: DrawStyle; scene: Scene _ viewerToolData.scene; viewerToolData.sceneStyleIndex _ IF viewerToolData.sceneStyleIndex = globalSceneStyleCount THEN 1 ELSE viewerToolData.sceneStyleIndex + 1; Labels.Set[viewerToolData.textSection.viewStyle, globalSceneStyleArray[viewerToolData.sceneStyleIndex]]; SELECT globalSceneStyleArray[viewerToolData.sceneStyleIndex] FROM "WireFrame" => newStyle _ wire; "Shaded" => newStyle _ shaded; "Normals" => newStyle _ normals; <<"RayCast" => newStyle _ rayCast;>> ENDCASE => ERROR; viewerToolData.camera.style _ newStyle; }; <> DoubleBuffer: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED { button: Buttons.Button _ NARROW[parent]; viewerToolData: ViewerToolData _ NARROW[clientData]; IF viewerToolData.doubleBuffer THEN { viewerToolData.doubleBuffer _ NOT viewerToolData.doubleBuffer; Buttons.SetDisplayStyle[button, $BlackOnWhite]; } ELSE { viewerToolData.doubleBuffer _ NOT viewerToolData.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]; IF viewerToolData.showCoordSys THEN { viewerToolData.showCoordSys _ NOT viewerToolData.showCoordSys; Buttons.SetDisplayStyle[button, $BlackOnWhite]; } ELSE { viewerToolData.showCoordSys _ NOT viewerToolData.showCoordSys; Buttons.SetDisplayStyle[button, $WhiteOnBlack]; }; }; -- end of ShowCoordsMode <> Selected: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED { <> viewerToolData: ViewerToolData _ NARROW[clientData]; solidViewer: Viewer _ viewerToolData.viewerPicture; InputFocus.SetInputFocus[solidViewer]; SVEditUser.ReSelectViewer[viewerToolData]; }; Deselected: PUBLIC PROC [viewerToolData: ViewerToolData] = TRUSTED { <> IF viewerToolData = NIL THEN RETURN; viewerToolData.textSection.isSelected _ FALSE; Labels.SetDisplayStyle[viewerToolData.textSection.selected, $BlackOnWhite, TRUE]; }; KillSelectionsButton: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED { viewerToolData: ViewerToolData _ NARROW[clientData]; SVSelections.KillSkitterAndSelections[viewerToolData.editToolData]; }; DeleteJacksButton: PUBLIC PROC [parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL] = TRUSTED { viewerToolData: ViewerToolData _ NARROW[clientData]; scene: Scene _ viewerToolData.scene; DisplayList3d.DeleteAllPrimitivesOfClass["jack", scene]; SVEditUser.PaintSceneAllViewers[SVViewerUser.EraseAndDrawSceneEtc, viewerToolData.editToolData, scene]; }; <<>> <> SingleRay: PUBLIC PROC [viewerToolData: ViewerToolData, cameraPoint: Point2d] = TRUSTED { f: IO.STREAM _ SVError.GetErrorStream[]; color: Color; xInt, yInt: INTEGER; tree: CSGTree; scene: Scene _ viewerToolData.scene; camera: Camera _ viewerToolData.camera; class: Classification; rayWorld: Ray; xInt _ Real.FixI[cameraPoint[1]]; yInt _ Real.FixI[cameraPoint[2]]; tree _ DisplayListToTree.AssemblyToTree[scene.assembly, 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]; f.PutChar[IO.CR]; [class, rayWorld] _ CastRays.SingleRay2[[xInt, yInt], tree, camera, FALSE]; ClassToStream[f, class]; f.PutChar[IO.CR]; CastRays.SortClassByPrimitive[class]; ClassToStream[f, class]; f.PutChar[IO.CR]; CSG.ReturnRayToPool[rayWorld]; CastRays.ReturnClassToPool[class]; f.PutChar[IO.CR]; }; ClassToStream: PROC [f: IO.STREAM, class: Classification] = { FOR i: NAT IN [1..class.count] DO f.PutF["(%g, %g) ", IO.rope[class.primitives[i].name], IO.real[class.params[i]]]; ENDLOOP; }; <> <> <> <> <> <> <> <> <> <> <> <> <> <> <<};>> <<>> DrawSlice: PUBLIC PROC [viewerToolData: ViewerToolData, slice: Slice] = { camera: Camera _ viewerToolData.camera; DoDrawSlice: PROC [dc: Graphics.Context] = TRUSTED { SVSlices.DrawSlice3d[dc, slice, camera]; }; SVViewerUser.Painter[DoDrawSlice, viewerToolData]; }; <> <> <> <> <> <> <> <<};>> <> <> <<[] _ Graphics.SetPaintMode[dc, opaque];>> <> <> <<};>> <> <> <<};>> << ComplementSelection: PUBLIC PROC [viewerToolData: ViewerToolData, selection: Selection, selectionType: SelectionType] = TRUSTED {>> <> <> <<};>> <> <<};>> <<>> DrawSelection: PROC [dc: Graphics.Context, viewerToolData: ViewerToolData, selection: Selection, selectionType: SelectionType, color: Color] = TRUSTED { <> <<[] _ Graphics.SetPaintMode[dc, invert];>> <> <> <> }; RayCastProgress: PUBLIC CastRays.NotifyOfProgressProc = TRUSTED { <> <> viewerToolData: ViewerToolData _ NARROW[clientData]; camera: Camera _ viewerToolData.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.