<<>> <> <> <> <> <<3d Tool: Line Drawing>> DIRECTORY Args, CedarProcess, Commander, Controls, Draw2d, G3dBasic, G3dControl, G3dDraw, G3dLight, G3dMatrix, G3dModel, G3dRender, G3dShape, G3dTool, G3dVector, Imager, ImagerArtwork, ImagerBackdoor, ImagerColor, ImagerFont, ImagerSample, IO, PFS, Rope, ViewerAbort, ViewerOps; G3dToolDrawImpl: CEDAR PROGRAM IMPORTS Args, Controls, Draw2d, G3dControl, G3dDraw, G3dMatrix, G3dModel, G3dRender, G3dShape, G3dTool, G3dVector, Imager, ImagerArtwork, ImagerBackdoor, ImagerFont, ImagerSample, IO, PFS, Rope, ViewerAbort, ViewerOps EXPORTS G3dTool ~ BEGIN <> ClickProc: TYPE ~ Controls.ClickProc; Viewer: TYPE ~ Controls.Viewer; Triple: TYPE ~ G3dBasic.Triple; Matrix: TYPE ~ G3dMatrix.Matrix; Shape: TYPE ~ G3dShape.Shape; Buffering: TYPE ~ G3dTool.Buffering; Client: TYPE ~ G3dTool.Client; DrawProc: TYPE ~ G3dTool.DrawProc; NamedBuffer: TYPE ~ G3dTool.NamedBuffer; Tool: TYPE ~ G3dTool.Tool; View: TYPE ~ G3dTool.View; ViewProc: TYPE ~ G3dTool.ViewProc; Context: TYPE ~ Imager.Context; Rectangle: TYPE ~ Imager.Rectangle; ROPE: TYPE ~ Rope.ROPE; <> maxDisplayTime: REAL ¬ 0.1; -- in seconds useCG6: BOOL ¬ TRUE; -- to debug, don't use CG6! Repaint: PUBLIC PROC [tool: Tool, whatChanged: REF ¬ $Client, buffering: Buffering ¬ default] ~ { Paint: ViewProc ~ {ViewerOps.PaintViewer[view.viewer, client, FALSE, whatChanged]}; IF tool.views = NIL OR tool.views.length = 0 THEN RETURN; tool.buffering ¬ buffering; IF whatChanged = $Camera AND tool.camera.mouse.state = up THEN RETURN; -- whatChanged ¬ $Store; G3dTool.DoWithViews[tool, Paint, whatChanged = $Camera]; IF tool.timing THEN G3dTool.PrintElapsedTime[tool, tool.previousDisplayTime, "Draw"]; tool.previousWhatChanged ¬ whatChanged; }; SetMinFPS: PUBLIC PROC [fps: NAT ¬ 4] ~ {maxDisplayTime ¬ 1.0/REAL[fps]}; <> <> <> <> <> <<};>> <<>> Store: PUBLIC PROC [context: Context, view: View, id: ATOM ¬ $Frame] ~ { Buffer[context, view, store, id]; }; Restore: PUBLIC PROC [context: Context, view: View, id: ATOM ¬ $Frame] ~ { Buffer[context, view, restore, id]; }; Buffer: PROC [context: Context, view: View, op: {store, restore}, id: ATOM] ~ { r: Rectangle; r ¬ view.tool.bounds ¬ ImagerBackdoor.GetBounds[context ! Imager.Error => GOTO Quit]; IF view # NIL THEN SELECT op FROM store => { Store: PROC [pm: Imager.PixelMap] ~ { nb: NamedBuffer ¬ [id, ImagerSample.Copy[pm[0]]]; FOR l: LIST OF NamedBuffer ¬ view.idBuffer, l.rest WHILE l # NIL DO IF l.first.id = id THEN { ImagerSample.ReleaseScratchMap[l.first.map]; l.first ¬ nb; RETURN; }; ENDLOOP; view.idBuffer ¬ CONS[nb, view.idBuffer]; }; ImagerBackdoor.AccessBufferRectangle[context, Store, r]; }; ENDCASE => FOR l: LIST OF NamedBuffer ¬ view.idBuffer, l.rest WHILE l # NIL DO Restore: PROC [pm: Imager.PixelMap] ~ {ImagerSample.Transfer[pm[0], l.first.map]}; IF l.first.id # id THEN LOOP; IF l.first.map # NIL THEN ImagerBackdoor.AccessBufferRectangle[context, Restore, r]; EXIT; ENDLOOP; EXITS Quit => NULL; -- ImagerBackdoor operations not defined for Interpress masters }; Draw: PUBLIC G3dTool.DrawProc ~ { IF v # NIL AND (whatChanged = $IPOut OR v.viewer = viewer) THEN WITH whatChanged SELECT FROM proc: REF CedarProcess.ForkableProc => [] ¬ proc[tool.context3d]; ENDCASE => SELECT whatChanged FROM $Store => Store[context, v]; $Restore => Restore[context, v]; ENDCASE => { <> <> <> <> <> <> <> <<};>> <> <> <> <> <<};>> <> <> <<[Real.Round[bounds.y], Real.Round[bounds.x]],>> <<[Real.Round[bounds.h], Real.Round[bounds.w]]];>> <> <<};>> ViewerAbortAction: PROC ~ {DoDraw[context, tool, whatChanged, viewer, v]}; IF tool.camera.use = view THEN { v.camera.matrix ¬ G3dMatrix.CopyMatrix[tool.camera.matrix, v.camera.matrix]; G3dControl.SaveState[tool.camera, v.camera]; }; SELECT whatChanged FROM NIL, $Camera, $DrawOps, $ShapeOps, $Scene => InvalidateVertexScreens[tool, all]; ENDCASE; IF tool.shapes # NIL THEN FOR n: NAT IN [0..tool.shapes.length) DO IF GetRenderStyle[tool.shapes[n]] # lines THEN { G3dTool.Render[tool, FALSE]; RETURN; }; ENDLOOP; ViewerAbort.CallWithAbortEnabled[viewer, ViewerAbortAction]; }; G3dRender.SetView[ tool.context3d, tool.camera.eyePoint, tool.camera.lookAt, tool.camera.fieldOfView.value, 0, tool.camera.up]; }; GetRenderStyle: PROC [s: Shape] RETURNS [rs: G3dRender.RenderStyle ¬ lines] ~ { d: G3dRender.RenderData ¬ G3dRender.RenderDataFrom[s]; IF d # NIL THEN rs ¬ d.renderStyle; }; DoDraw: PROC [context: Context, clientData, whatChanged: REF, viewer: Viewer, v: View] ~ { <> DrawSprite: PROC ~ {G3dDraw.Mark[context, t.sprite, x, vp,, asterisk]}; DrawAxes: PROC ~ { Vec: PROC [v: Triple, r: ROPE] ~ {G3dDraw.Vector[context, [], v, x, vp, r]}; Vec[[1, 0, 0], "x"]; Vec[[0, 1, 0], "y"]; Vec[[0, 0, 1], "z"]; }; LabelVertex: PROC ~ { vtx: G3dShape.Vertex ¬ t.shapes[t.selectedShape].vertices[t.selectedVertex]; r: ROPE ¬ IO.PutFLR[" #%g (%g, %g, %g)", LIST[ IO.int[t.selectedVertex], IO.real[vtx.point.x], IO.real[vtx.point.y], IO.real[vtx.point.z]]]; IF t.shapes.length#1 THEN r ¬IO.PutFR["%g (shape %g)", [rope[r]], IO.int[t.selectedVertex]]; Imager.SetFont[context, ImagerFont.Find["xerox/tiogafonts/helvetica12"]]; G3dDraw.Vector[context, vtx.point, vtx.normal, x, vp, r,, 30.0, asterisk]; }; IdentifyView: PROC ~ { NViews: PROC RETURNS [i: INT ¬ 0] ~ { FOR l: LIST OF NAT ¬ t.activeViews ,l.rest WHILE l#NIL DO i ¬ i+1; ENDLOOP; }; active: BOOL ¬ FALSE; IF whatChanged = $IPOut THEN RETURN; IF t.shapes = NIL AND NViews[] = 1 THEN RETURN; IF t.camera.use = view THEN FOR l: LIST OF NAT ¬ t.activeViews, l.rest WHILE l # NIL DO IF v.index = l.first THEN {active ¬ TRUE; EXIT}; ENDLOOP; G3dDraw.SetColor[context, [0.0, 0.0, 0.0]]; IF active THEN { G3dDraw.Quadrangle[context, 0, 0, 0, 20, 20, 20, 20, 0, cg6]; G3dDraw.SetColor[context, [1.0, 1.0, 1.0]]; } ELSE { G3dDraw.Line2d[context, [0, 20], [20, 20], solid, cg6]; G3dDraw.Line2d[context, [20, 0], [20, 20], solid, cg6]; G3dDraw.SetColor[context, [0.0, 0.0, 0.0]]; }; Draw2d.Label[context, [5, 5], IO.PutFR1["%g", IO.int[v.index]]]; }; ClientsDraw: PROC [whatChanged: REF ANY] ~ { IF NOT t.drawClient THEN RETURN; IF t.client.draw # NIL THEN t.client.draw[context, viewer, whatChanged, x, vp, v, t, t.client.data]; IF t = G3dTool.GetActiveTool[] THEN FOR c: LIST OF Client ¬ G3dTool.GetActiveClients[], c.rest WHILE c # NIL DO IF c.first.draw # NIL THEN c.first.draw[context, viewer, whatChanged, x, vp, v, t, c.first.data]; ENDLOOP; }; DrawShapesLights: PROC ~ { DrawCertainShapes: PROC [which: {selected, unselected, all}, type: Draw2d.DrawType]~{ lv: Triple ¬ IF t.lights#NIL AND t.lights.length>0 THEN t.lights[0].direction ELSE [.7,.7,.0]; IF t.shapes # NIL THEN FOR n: NAT IN [0..t.shapes.length) DO s: Shape ¬ t.shapes[n]; IF s # NIL AND s.visible AND (which = all OR s.selected = (which=selected)) THEN { sv: G3dDraw.Options ¬ t.drawOptions; t.drawOptions.lineType ¬ type; IF NOT G3dShape.FaceValid[s, normal] THEN G3dShape.SetFaceNormals[s]; IF NOT s.showBackfaces AND sv.backFaces = on THEN t.drawOptions.backFaces ¬ off; v.screens[n] ¬ G3dDraw.Shape[context, s, x,, vp, lv, v.screens[n],, t.drawOptions]; t.drawOptions ¬ sv; }; ENDLOOP; }; -- IF t.ops.shape THEN -- DrawCertainShapes[unselected, dashed]; -- IF t.ops.shape THEN -- DrawCertainShapes[selected, solid]; IF t.shapes # NIL AND t.ops.light AND t.lights # NIL THEN { DrawSun: PROC [x, y: INTEGER, fill: BOOL] ~ { DrawQuad: PROC [x1, y1, x2, y2, x3, y3, x4, y4: INTEGER] ~ { IF fill THEN G3dDraw.Quadrangle[context, x1, y1, x2, y2, x3, y3, x4, y4, cg6] ELSE { G3dDraw.Line2d[context, [x1, y1], [x2, y2], solid, cg6]; G3dDraw.Line2d[context, [x2, y2], [x3, y3], solid, cg6]; G3dDraw.Line2d[context, [x3, y3], [x4, y4], solid, cg6]; }; }; x1, x2, x3, x4, y1, y2, y3, y4: INTEGER; x1 ¬ x-7; x2 ¬ x-3; x3 ¬ x+3; x4 ¬ x+7; y1 ¬ y-5; y2 ¬ y+5; y3 ¬ y1-7; y4 ¬ y2+7; DrawQuad[x1, y, x2, y1, x3, y1, x4, y]; DrawQuad[x1, y, x2, y2, x3, y2, x4, y]; G3dDraw.Line2d[context, [x1, y], [x-14, y], solid, cg6]; G3dDraw.Line2d[context, [x4, y], [x+14, y], solid, cg6]; G3dDraw.Line2d[context, [x2, y1], [x1, y3], solid, cg6]; G3dDraw.Line2d[context, [x3, y1], [x4, y3], solid, cg6]; G3dDraw.Line2d[context, [x2, y2], [x1, y4], solid, cg6]; G3dDraw.Line2d[context, [x3, y2], [x4, y4], solid, cg6]; }; hasPersp: BOOL ¬ G3dMatrix.HasPerspective[x]; IF t.drawLights THEN FOR i: INTEGER IN [0..t.lights.length) DO l: G3dLight.Light ¬ t.lights[i]; screen: G3dBasic.Screen ¬ G3dMatrix.GetScreen[l.position, x, hasPersp, vp]; r: Triple ¬ IF G3dVector.FrontFacing[l.direction, l.position, x] THEN [1,0,0] ELSE [0,0,1]; G3dDraw.SetColor[context, r]; l.screen ¬ screen.pos; DrawSun[screen.intPos.x, screen.intPos.y, i = t.selectedLight]; G3dDraw.Vector[context, l.position, l.direction, x, vp]; ENDLOOP; G3dDraw.SetColor[context, [0.0, 0.0, 0.0]]; }; }; Clear: PROC ~ { color: ImagerColor.RGB ¬ t.context3d.backgroundColor; IF whatChanged # $IPOut THEN G3dDraw.Clear[context, t.background]; }; Action: PROC ~ { SELECT whatChanged FROM $Clear => Clear[]; $Vertex => {Restore[context, v, $Pose]; IF vertex THEN LabelVertex[]}; $Sprite => IF t.drawSprite THEN {Restore[context, v, $Pose]; DrawSprite[]}; NIL, $Camera, $DrawOps, $IPOut, $ShapeOps, $Scene, $Stuff, $Client, $SelectShape, $SelectLight, $Pose => { <> Clear[]; IdentifyView[]; <> Imager.SetStrokeWidth[context, t.ipStrokeWidth]; G3dDraw.SetColor[context, [0.0, 0.0, 0.0]]; ClientsDraw[whatChanged]; DrawShapesLights[]; IF t.drawAxes THEN DrawAxes[]; IF t.drawPendant THEN G3dDraw.Pendant[context, x, .07, .7, .7, ["x", "y", "z",,,]]; IF vertex THEN LabelVertex[]; IF t.drawSprite THEN DrawSprite[]; IF whatChanged = NIL OR whatChanged = $Pose THEN Store[context, v]; }; $View => IdentifyView[]; ENDCASE => { ClientsDraw[whatChanged]; RETURN; }; ClientsDraw[$Overlay]; }; t: Tool ¬ NARROW[clientData]; x: Matrix ¬ v.camera.matrix; vp: G3dMatrix.Viewport ¬ v.viewport; vertex: BOOL ¬ t.queryVertices AND t.selectedShape # -1 AND t.selectedVertex # -1; cg6: G3dDraw.UseCG6 ¬ IF useCG6 AND whatChanged # $IPOut AND NOT t.drawOptions.tapered AND NOT t.drawOptions.dimFurtherLines AND G3dDraw.CG6Available[] THEN y ELSE n; IF cg6 = y THEN G3dDraw.WrapInCG6[context, Action] ELSE Action[]; }; InvalidateVertexScreens: PUBLIC PROC [t: Tool, which: G3dTool.WhichVertices] ~ { IF t.shapes # NIL THEN FOR n: NAT IN [0..t.shapes.length) DO s: Shape ¬ t.shapes[n]; IF s # NIL AND (which = all OR s.selected) THEN { renderData: G3dRender.RenderData ¬ G3dRender.RenderDataFrom[s]; IF t.views # NIL THEN FOR i: NAT IN [0..t.views.length) DO screens: G3dTool.ScreenSequence ¬ t.views[i].screens[n]; screens.screensValid ¬ screens.extentValid ¬ FALSE; ENDLOOP; }; ENDLOOP; }; <> DrawOpsButton: PUBLIC ClickProc ~ { save: BOOL ¬ FALSE; t: Tool ¬ NARROW[clientData]; choice: INT ¬ Controls.PopUpRequest[["Draw Ops"], LIST[ -- 1 -- Controls.BoolRequest[t.drawOptions.flatShade, "Flat Shade"], -- 2 -- Controls.BoolRequest[t.drawOptions.hiddenLineElim, "Hidden Line Elimination"], -- 3 -- Controls.BoolRequest[t.drawOptions.vertexNormals, "Vertex Normals"], -- 4 -- Controls.BoolRequest[t.drawOptions.faceNormals, "Face Normals"], -- 5 -- Controls.BoolRequest[t.drawOptions.verticesOnly, "Vertices Only"], -- 6 -- Controls.BoolRequest[t.drawOptions.labelVertices, "Label Vertices"], -- 7 -- Controls.BoolRequest[t.drawOptions.labelPolygons, "Label Polygons"], -- 8 -- Controls.BoolRequest[t.drawSprite, "Sprite"], -- 9 -- Controls.BoolRequest[t.drawPendant, "Pendant"], -- 10 -- Controls.BoolRequest[t.drawClient, "Client Display"], -- 11 -- Controls.BoolRequest[t.timing, "Timing"], -- 12 -- Controls.BoolRequest[t.drawOptions.silhouettesOnly, "Silhouettes"], -- 13 -- Controls.BoolRequest[t.drawOptions.backFaces=dashed, "Dashed Backfaces"], -- 14 -- Controls.BoolRequest[t.drawCurves, "Curves"], -- 15 -- Controls.BoolRequest[t.queryVertices, "Vertex Querying"], -- 16 -- Controls.BoolRequest[t.drawOptions.tapered, "Tapered lines"], -- 17 -- Controls.BoolRequest[t.drawOptions.dimFurtherLines, "Dim far lines"], -- 18 -- ["SetDrawMode", "Line Drawing Mode (will prompt)"], -- 19 -- ["Stuff", "Stuff to Tioga selection"], -- 20 -- ["Write Vectors", "Write 2d vectors to a file"]]]; SELECT choice FROM 1 => t.drawOptions.flatShade ¬ NOT t.drawOptions.flatShade; 2 => t.drawOptions.hiddenLineElim ¬ NOT t.drawOptions.hiddenLineElim; 3 => t.drawOptions.vertexNormals ¬ NOT t.drawOptions.vertexNormals; 4 => t.drawOptions.faceNormals ¬ NOT t.drawOptions.faceNormals; 5 => t.drawOptions.verticesOnly ¬ NOT t.drawOptions.verticesOnly; 6 => t.drawOptions.labelVertices ¬ NOT t.drawOptions.labelVertices; 7 => t.drawOptions.labelPolygons ¬ NOT t.drawOptions.labelPolygons; 8 => IF (t.drawSprite ¬ NOT t.drawSprite) THEN save ¬ TRUE; 9 => t.drawPendant ¬ NOT t.drawPendant; 10 => t.drawClient ¬ NOT t.drawClient; 11 => t.timing ¬ NOT t.timing; 12 => t.drawOptions.silhouettesOnly ¬ NOT t.drawOptions.silhouettesOnly; 13 => t.drawOptions.backFaces ¬ IF t.drawOptions.backFaces = dashed THEN on ELSE dashed; 14 => { t.drawCurves ¬ NOT t.drawCurves; IF t.drawCurves AND t.shapes # NIL THEN FOR n: NAT IN [0..t.shapes.length) DO m: G3dModel.Model ¬ G3dModel.GetModel[t.shapes[n]]; IF m.curves = NIL THEN G3dModel.SetCurves[t.shapes[n]]; ENDLOOP; }; 15 => IF (t.queryVertices ¬ NOT t.queryVertices) THEN save ¬ TRUE; 16 => t.drawOptions.tapered ¬ NOT t.drawOptions.tapered; 17 => t.drawOptions.dimFurtherLines ¬ NOT t.drawOptions.dimFurtherLines; 18 => { choice: INT ¬ Controls.MultiRequest["Line Drawing Mode is ", LIST[ ["Solid", t.drawOptions.lineType = solid AND NOT t.drawOptions.verticesOnly], ["Dashed", t.drawOptions.lineType = dashed AND NOT t.drawOptions.verticesOnly], ["Dotted", t.drawOptions.lineType = dotted AND NOT t.drawOptions.verticesOnly]]]; t.drawOptions.lineType ¬ SELECT choice FROM 2 => dashed, 3 => dotted, ENDCASE => solid; }; 19 => { DoStuff: PROC [context: Context] ~ { Inner: PROC ~ {G3dTool.Draw[context, NIL, $Stuff, NIL, [], NIL, t, t]}; Imager.TranslateT[context, [-t.bounds.x, -t.bounds.y]]; Imager.DoSaveAll[context, Inner]; }; ImagerArtwork.PasteArtwork[DoStuff, t.bounds, ImagerArtwork.Points[],,, FALSE]; }; 20 => { fileName: ROPE ¬ Controls.TypescriptReadFileName[t.typescript]; IF fileName # NIL AND t.shapes # NIL THEN { out: IO.STREAM ¬ PFS.StreamOpen[PFS.PathFromRope[fileName], create]; v: View ¬ G3dTool.GetView[NIL, t]; IO.PutF1[t.cmd.out, "Writing %g\n", IO.rope[fileName]]; FOR n: NAT IN [0..v.screens.length) DO s: Shape ¬ t.shapes[n]; screens: G3dTool.ScreenSequence ¬ v.screens[n]; FOR i: NAT IN [0..s.edges.length) DO e: G3dShape.Edge ¬ s.edges[i]; p0: G3dBasic.Pair ¬ screens[e.v0].pos; p1: G3dBasic.Pair ¬ screens[e.v1].pos; IO.PutFL[out, "%6.5f\t%6.5f\t%6.5f\t%6.5f\n", LIST[IO.real[p0.x], IO.real[p0.y], IO.real[p1.x], IO.real[p1.y]]]; ENDLOOP; ENDLOOP; IO.Close[out]; }; }; ENDCASE; IF save THEN G3dTool.Repaint[t, $Save]; IF Controls.GetPopUpButton[] = right AND choice > 0 AND choice # 10 THEN Repaint[t, $DrawOps]; }; <> UseCG6: Commander.CommandProc ~ { SELECT TRUE FROM Rope.Equal[Args.GetRope[cmd], "yes"] => useCG6 ¬ TRUE; Rope.Equal[Args.GetRope[cmd], "no"] => useCG6 ¬ FALSE; Rope.Equal[Args.GetRope[cmd], "?"] => IO.PutF1[cmd.out, "useCG6 = %g\n", IO.rope[IF useCG6 THEN "yes" ELSE "no"]]; ENDCASE => IO.PutRope[cmd.out, "specify yes, no, or ?\n"]; }; G3dTool.Register["UseCG6", UseCG6, "enable or disable CG6"]; END. .. <> <> <> <> <> <> <<};>> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> < 0.0 THEN xrot ELSE -xrot]];>> <> < 0.0 THEN -yrot ELSE yrot]];>> <<};>> <> <> <> <> <> <> <> <<};>> <<>> doubleBuffer: BOOL ¬ FALSE; <> <<(t.previousDisplayTime < maxDisplayTime OR t.drawOptions.silhouettesOnly));>> <<>> IF NOT doubleBuffer THEN { <> <> Imager.SetColor[context, Imager.white]; <> Imager.SetColor[context, Imager.black]; }; <> <> IF doubleBuffer THEN { mn: SF.Vec ¬ SF.Min[v.extent.min, v.previousExtent.min]; mx: SF.Vec ¬ SF.Max[v.extent.max, v.previousExtent.max]; Imager.DoWithBuffer[context, A, mn.f, mn.s, mx.f-mn.f, mx.s-mn.s, Imager.white]; } ELSE ViewerAbort.CallWithAbortEnabled[viewer, A]; G3dTool.StartTimer[t]; t.previousDisplayTime ¬ G3dTool.ElapsedTime[t]; noneSelected: BOOL ¬ t.selectedShape = -1 AND t.focusNode = NIL; <<$SelectShape, $Pose => {>> <> <> <> <> <> <> <> <<};>> <> <> <<};>> <<>> SetScreensGetExtent: PROC [t: Tool, v: View, context: Context, whatChanged: REF ANY] RETURNS [b: SF.Box] ~ { clientDraw: BOOL ¬ t.client.draw # NIL; fullBox: SF.Box ¬ [[0, 0], [10000, 10000]]; IF NOT clientDraw AND t = G3dTool.GetActiveTool[] THEN FOR c: LIST OF Client ¬ G3dTool.GetActiveClients[], c.rest WHILE c # NIL DO IF c.first.draw # NIL THEN {clientDraw ¬ TRUE; EXIT}; ENDLOOP; SELECT whatChanged FROM $Camera, $DrawOps, $ShapeOps, $Scene, $Pose, NIL => { rb: G2dBasic.Box ¬ [[10000, 10000], [-10000, -10000]]; IF v.context3d.shapes = NIL THEN RETURN[[[0, 0], [0, 0]]]; FOR n: NAT IN [0..t.shapes.length) DO s: Shape ¬ t.shapes[n]; IF s.visible THEN { scr: G3dShape.ScreenSequence ¬ v.screens[n]; IF scr = NIL OR NOT scr.screensValid THEN v.screens[n] ¬ scr ¬ G3dShape.SetScreenCoords[context, s, v.camera.matrix, scr, TRUE]; rb.min ¬ [MIN[rb.min.x, scr.extent.min.x], MIN[rb.min.y, scr.extent.min.y]]; rb.max ¬ [MAX[rb.max.x, scr.extent.max.x], MAX[rb.max.y, scr.extent.max.y]]; }; ENDLOOP; b.min ¬ [Real.Round[rb.min.y], Real.Round[rb.min.x]]; b.max ¬ [Real.Round[rb.max.y], Real.Round[rb.max.x]]; }; $IPOut, $Stuff, $SelectShape, $View => RETURN[v.extent]; ENDCASE => RETURN[fullBox]; IF clientDraw THEN RETURN[fullBox]; IF t.queryVertices AND t.selectedShape # -1 AND t.selectedVertex # -1 THEN b ¬ [SF.Sub[b.min, [5, 5]], SF.Add[b.max, [10, 10]]]; };