SVRefreshImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Last edited by Bier on February 2, 1987
Contents: All painting actions in Solidviews are called thru this interface.
DIRECTORY
Atom, BufferedRefresh, CodeTimer, Feedback, FunctionCache, GGBoundBox, GGViewerOps, Imager, ImagerPath, ImagerTransformation, PriorityQueue, Real, Rope, SV2d, SV3d, SVAlign, SVAssembly, SVBasicTypes, SVBoundBox, SVCaret, SVCoordSys, SVDraw3d, SVFiles, SVGraphics, SVImage, SVInterfaceTypes, SVMatrix3d, SVModelTypes, SVPolygon3d, SVPreprocess3d, SVRefresh, SVScene, SVSceneToTree, SVSceneTypes, SVSelect, SVSelections, SVState, SVUtility;
SVRefreshImpl:
CEDAR
PROGRAM
IMPORTS Atom, BufferedRefresh, CodeTimer, Feedback, FunctionCache, GGBoundBox, GGViewerOps, Imager, ImagerTransformation, PriorityQueue, Rope, SVAlign, SVAssembly, SVBoundBox, SVCaret, SVCoordSys, SVDraw3d, SVFiles, SVGraphics, SVImage, SVMatrix3d, SVPreprocess3d, SVScene, SVSceneToTree, SVSelect, SVSelections, SVState, SVUtility
EXPORTS SVRefresh =
BEGIN
AlignmentLine: TYPE = SVInterfaceTypes.AlignmentLine;
AlignmentObject: TYPE = SVSceneTypes.AlignmentObject;
AlignmentPoint: TYPE = SVInterfaceTypes.AlignmentPoint;
ArtworkToolData: TYPE = SVInterfaceTypes.ArtworkToolData;
AssemblyGenerator: TYPE = SVScene.AssemblyGenerator;
SliceList: TYPE = SVSceneTypes.SliceList;
BoundBox: TYPE = SVBasicTypes.BoundBox;
Camera: TYPE = SVModelTypes.Camera;
Color: TYPE = Imager.Color;
CoordSysGenerator: TYPE = SVScene.CoordSysGenerator;
CoordSystem: TYPE = SVModelTypes.CoordSystem;
CSGTree: TYPE = SVSceneTypes.CSGTree;
EditToolData: TYPE = SVInterfaceTypes.EditToolData;
FeatureData: TYPE = SVInterfaceTypes.FeatureData;
FrameBox: TYPE = SVModelTypes.FrameBox;
Matrix4by4: TYPE = SVModelTypes.Matrix4by4;
PlanarSurface: TYPE = SVSceneTypes.PlanarSurface;
Point2d: TYPE = SV2d.Point2d;
Point3d: TYPE = SV3d.Point3d;
Rectangle: TYPE = Imager.Rectangle;
Sandwich: TYPE = BufferedRefresh.Sandwich;
Scene: TYPE = SVSceneTypes.Scene;
Selection: TYPE = SVInterfaceTypes.Selection;
SelectionClass: TYPE = SVSelect.SelectionClass;
SelectionGenerator: TYPE = SVInterfaceTypes.SelectionGenerator;
SelectionType: TYPE = SVInterfaceTypes.SelectionType; -- {target, movee, plane};
Shape: TYPE = SVSceneTypes.Shape;
Skitter: TYPE = SVSceneTypes.Skitter;
Slice: TYPE = SVSceneTypes.Slice;
SliceDescriptor: TYPE = SVSceneTypes.SliceDescriptor;
SliceDescriptorGenerator: TYPE = SVSceneTypes.SliceDescriptorGenerator;
SliceParts: TYPE = SVSceneTypes.SliceParts;
ToolData: TYPE = SVSceneTypes.ToolData;
SVData: TYPE = SVInterfaceTypes.SVData;
CreateSandwich:
PUBLIC
PROC []
RETURNS [sandwich: Sandwich] = {
sandwich ← BufferedRefresh.CreateSandwich[
LIST[
[$Background, TRUE, RefreshBackground], -- back ...
[$Overlay, FALSE, RefreshOverlay],
[$SelectionPlane, FALSE, RefreshSelectionPlane],
[$Foreground, TRUE, RefreshForeground],
[$SkitterPlane, FALSE, RefreshSkitterPlane]]]; -- ... to front
ActionAreaPaint:
PUBLIC
PROC [screen: Imager.Context, whatHasChanged:
ATOM, svData: SVData] = {
DoActionAreaPaint[screen, whatHasChanged, svData];
};
DoActionAreaPaint:
PROC [screen: Imager.Context, whatHasChanged:
ATOM, svData: SVData] = {
whatHasChanged will be an atom describing some change which has occurred to the viewable scene state, such as $CaretMoved, $OverlayMoved, $ObjectAdded, $SelectionChanged, or $Everything. A pointer to the particular objects to be repainted will be stored in svData for now.
We envision a scheme where Dispatch may actually queue up painting jobs and attempt optimizations on the queue. In this case, the objects to be repainted will have to be stored separately from the svData.
showColors: BOOL;
IF svData.refresh.suppressRefresh THEN RETURN;
showColors ← SVState.GetShowColors[svData];
SELECT whatHasChanged
FROM
$None => NULL;
$PaintEntireScene, $ViewersPaintEntireScene => PaintEntireScene[screen, svData, showColors];
$ViewersPaintAllPlanes => PaintAllPlanes[screen, svData, showColors, FALSE, FALSE];
$NewAlignmentsDeselected => PaintAllPlanes[screen, svData, showColors, FALSE, FALSE];
$NewAlignmentsSelected => PaintAllPlanes[screen, svData, showColors, FALSE, FALSE];
$SequencesMadeHot => PaintAllPlanes[screen, svData, showColors, FALSE, FALSE];
$SequencesMadeCold => PaintAllPlanes[screen, svData, showColors, FALSE, FALSE];
$SelectionChanged => SelectionOrCaretChanged[screen, svData, showColors];
$FinishedAdding => FinishedAdding[screen, svData, showColors];
$FinishedDragging => FinishedDragging[screen, svData, showColors];
$SkitterMoved => SelectionOrCaretChanged[screen, svData, showColors];
$AnchorAdded => PaintAllPlanes[screen, svData, showColors, FALSE, FALSE];
$AnchorRemoved => PaintAllPlanes[screen, svData, showColors, FALSE, FALSE];
$FrameChanged => PaintAllPlanes[screen, svData, showColors, FALSE, FALSE];
$DrawPlane => PaintAllPlanes[screen, svData, showColors,
FALSE,
FALSE];
Dragging
$DuringMotion => PaintDragOverlay[screen, svData, TRUE, showColors];
$DuringSkitterPos => PaintDragOverlay[screen, svData, FALSE, showColors];
$DuringSelect => DuringSelect[screen, svData, showColors];
$ObjectChangedInPlace => ObjectChangedInPlace[screen, svData, normal, showColors];
$ObjectChangedBoundBoxProvided => PaintEntireScene[screen, svData, showColors];
$ObjectAdded => PaintEntireScene[screen, svData, showColors];
Debugging and Draw Directly on the Screen
$DrawBlackAndWhite => DrawBlackAndWhite[screen, svData];
$DrawColor => DrawColor[screen, svData];
$DrawCoordSystems => DrawCoordSystems[screen, svData];
$DrawBoundBoxes => DrawBoundBoxes[screen, svData];
$DrawBoundSpheres => DrawBoundSpheres[screen, svData];
$DrawRayCastProgress => DrawRayCastProgress[screen, svData];
$DrawCrossHairs => DrawCrossHairs[screen, svData];
$DrawPt => DrawPt[screen, svData];
$DrawPaint => DrawPaint[screen, svData];
$DrawCatScan => DrawCatScan[screen, svData];
$PaintSpot => PaintSpot[screen, svData];
$PaintHitLine => PaintHitLine[screen, svData];
ENDCASE => {
Feedback.Append[svData.feedback, Rope.Cat["SVRefreshImpl doesn't know how to ", Atom.GetPName[whatHasChanged], "."], oneLiner];
Feedback.Blink[svData.feedback];
};
};
DrawRayCastProgress:
PROC [screen: Imager.Context, svData: SVData] = {
camera: Camera ← svData.camera;
width: REAL ← 10;
box: Imager.Rectangle;
lowLeft, upRight: Point2d;
currentY, minX, minY, maxX, maxY: REAL;
currentY ← svData.refresh.currentY;
minX ← svData.refresh.minX;
minY ← svData.refresh.minY;
maxX ← svData.refresh.maxX;
maxY ← svData.refresh.maxY;
lowLeft ← SVCoordSys.CameraToScreen[[minX, minY], camera.screenCS];
upRight ← SVCoordSys.CameraToScreen[[maxX, currentY], camera.screenCS];
box ← [lowLeft[1], lowLeft[2], upRight[1] - lowLeft[1], upRight[2] - lowLeft[2]];
Imager.SetColor[screen, Imager.black];
Imager.MaskRectangle[screen, box];
};
PaintEntireScene:
PROC [screen: Imager.Context, svData: SVData, showColors:
BOOL] = {
sandwich: BufferedRefresh.Sandwich ← svData.refresh.sandwich;
CodeTimer.StartInt[$PaintEntireScene, $Solidviews];
BufferedRefresh.SetLayerOK[sandwich, $Foreground, FALSE];
BufferedRefresh.SetLayerOK[sandwich, $Background, FALSE];
PaintAllPlanes[screen, svData, showColors, FALSE, FALSE];
CodeTimer.StopInt[$PaintEntireScene, $Solidviews];
};
PaintAllPlanes:
PROC [screen: Imager.Context, svData: SVData, showColors:
BOOL, caretIsMoving:
BOOL, dragInProgress:
BOOL] = {
sandwich: BufferedRefresh.Sandwich ← svData.refresh.sandwich;
clientToViewer, viewerToClient: Imager.Transformation;
ignoreBackingMap: BOOL;
IF svData.refresh.suppressRefresh THEN RETURN;
svData.refresh.caretIsMoving ← caretIsMoving;
svData.refresh.dragInProgress ← dragInProgress;
clientToViewer ← viewerToClient ← ImagerTransformation.Scale[1.0];
ignoreBackingMap ← showColors;
BufferedRefresh.DrawSandwich[sandwich, screen, clientToViewer, viewerToClient, svData, ignoreBackingMap];
};
Single-Plane Commands
RefreshBackground:
PROC [dc: Imager.Context, boundRect: Rectangle, clientData:
REF
ANY] = {
svData: SVData ← NARROW[clientData];
camera: Camera ← svData.camera;
lo, hi: Point2d;
lo ← [boundRect.x, boundRect.y]; -- in screen coords
hi ← [boundRect.x + boundRect.w, boundRect.y + boundRect.h];
lo ← SVCoordSys.ScreenToCamera[lo, camera.screenCS];
hi ← SVCoordSys.ScreenToCamera[hi, camera.screenCS];
DrawObjectsFiltered[dc, svData, GGBoundBox.CreateBoundBox[lo[1], lo[2], hi[1], hi[2]], FALSE, -Real.LargestNumber];
};
RefreshOverlay:
PROC [dc: Imager.Context, boundRect: Rectangle, clientData:
REF
ANY] = {
This could use some optimizing.
DrawDragOverlayAux:
PROC = {
IF svData.refresh.orderedOverlayList=NIL THEN svData.refresh.orderedOverlayList ← OrderOverlayList[svData]; -- update ordered list
SELECT camera.style
FROM
wire, normals => {
FOR list:
LIST
OF SliceDescriptor ← svData.refresh.orderedOverlayList, list.rest
UNTIL list =
NIL
DO
SVAssembly.DrawTransform[list.first, dc, scene, camera, svData.drag.transform];
ENDLOOP;
};
shaded, hiddenLine => {
predictedBufferSize: NAT ← CountSurfacesInAssembly[scene.assembly];
polygonBuffer: PriorityQueue.Ref ←
PriorityQueue.Predict[predictedBufferSize, DeeperPlanarPolygon, camera];
eyeWorld: Point3d←SVGraphics.LocalToWorld[[0,0,camera.focalLength], camera.coordSys];
FOR list:
LIST
OF SliceDescriptor ← svData.refresh.orderedOverlayList, list.rest
UNTIL list =
NIL
DO
SVAssembly.AddPolygonsToBufferTransform[polygonBuffer, list.first, scene, camera, eyeWorld, svData.drag.transform];
ENDLOOP;
SVAssembly.DrawBuffer[dc, polygonBuffer, scene, camera, camera.style=hiddenLine];
};
ENDCASE => ERROR;
};
svData: SVData ← NARROW[clientData];
scene: Scene ← svData.scene;
camera: Camera ← svData.camera;
filter: BoundBox ← SVBoundBox.BoundBoxFromRectangle[boundRect, camera];
IF svData.refresh.suppressRefresh THEN RETURN;
Imager.DoSaveAll[dc, DrawDragOverlayAux];
};
RefreshSelectionPlane:
PROC [dc: Imager.Context, boundRect: Rectangle, clientData:
REF
ANY] = {
svData: SVData ← NARROW[clientData];
camera: Camera ← svData.camera;
scene: Scene ← svData.scene;
caretIsMoving: BOOL ← svData.refresh.caretIsMoving;
dragInProgress: BOOL ← svData.refresh.dragInProgress;
IF svData.refresh.suppressRefresh THEN RETURN;
IF svData.camera.quality#quality
THEN {
DrawAttractorFeedback[dc, svData, dragInProgress, caretIsMoving];
DrawCpsOfSelectedSlices[dc, scene, camera, dragInProgress, caretIsMoving];
};
Draw Coordinate Frames and Old Fashioned Selections (Solidviews only)
IF caretIsMoving OR dragInProgress THEN RETURN;
Imager.SetColor[dc, Imager.black];
IF svData.showCoordSys
THEN
DrawCoordSystems[dc, svData];
DrawAnySelectionsDCAux[dc, movee, svData, scene, camera];
DrawAnySelectionsDCAux[dc, target, svData, scene, camera];
DrawAnySelectionsDCAux[dc, plane, svData, scene, camera];
};
RefreshForeground:
PROC [dc: Imager.Context, boundRect: Rectangle, clientData:
REF
ANY] = {
svData: SVData ← NARROW[clientData];
IF SVState.GetSuppressRefresh[svData] THEN RETURN;
FunctionCache.Flush[svData.refresh.lineCache];
SVAlign.DrawAlignBagRegardless[dc, svData.hitTest.alignBag, svData];
};
RefreshSkitterPlane:
PROC [dc: Imager.Context, boundRect: Rectangle, clientData:
REF
ANY] = {
svData: SVData ← NARROW[clientData];
camera: Camera ← svData.camera;
Imager.SetColor[dc, Imager.black];
DrawSkitter[dc, svData.editToolData, camera];
DrawAnchor[dc, svData, camera];
};
NoteNewForeground:
PUBLIC
PROC [alignObjects:
LIST
OF FeatureData, svData: SVData] = {
Add these new features to the foreground context.
Paint
Foreground:
PROC = {
SVAlign.DrawFeatureList[foregroundContext, alignObjects, svData];
};
foregroundContext: Imager.Context ← BufferedRefresh.GetLayerContext[svData.refresh.sandwich, $Foreground];
Imager.DoSave[foregroundContext, PaintForeground];
BufferedRefresh.SetLayerOK[svData.refresh.sandwich, $Foreground, TRUE];
};
Drawing Utilities
DrawAttractorFeedback:
PROC [dc: Imager.Context, svData: SVData, dragInProgress, caretIsMoving:
BOOL] = {
Draws control points on slices which are the caret attractor.
skitter: Skitter ← svData.editToolData.skitter;
scene: Scene ← svData.scene;
camera: Camera ← svData.camera;
attractor: AlignmentObject ← SVCaret.GetAttractor[skitter];
IF attractor#
NIL
THEN {
WITH attractor SELECT FROM
sliceD: SliceDescriptor => {
selectedD: SliceDescriptor ← SVSelect.FindSelectedSlice[sliceD.slice, scene, normal];
selectedParts: SliceParts ← IF selectedD = NIL THEN NIL ELSE selectedD.parts;
SVAssembly.DrawAttractorFeedback[sliceD, selectedParts, dragInProgress, dc, svData.camera];
};
alignLine: AlignmentLine => {
FOR list:
LIST
OF Point3d ← alignLine.triggerPoints, list.rest
UNTIL list =
NIL
DO
SVDraw3d.DrawGlow[dc, list.first, camera];
ENDLOOP;
};
alignPoint: AlignmentPoint => {
IF alignPoint.curve1 # NIL THEN DrawGlowPoints[dc, alignPoint.curve1, camera, scene, dragInProgress];
IF alignPoint.curve2 # NIL THEN DrawGlowPoints[dc, alignPoint.curve2, camera, scene, dragInProgress];
};
ENDCASE;
};
};
DrawGlowPoints:
PROC [dc: Imager.Context, feature: FeatureData, camera: Camera, scene: Scene, dragInProgress:
BOOL] = {
WITH feature.shape
SELECT
FROM
alignLine: AlignmentLine => {
FOR list:
LIST
OF Point3d ← alignLine.triggerPoints, list.rest
UNTIL list =
NIL
DO
SVDraw3d.DrawGlow[dc, list.first, camera];
ENDLOOP;
};
sliceD: SliceDescriptor => {
selectedD: SliceDescriptor ← SVSelect.FindSelectedSlice[sliceD.slice, scene, normal];
selectedParts: SliceParts ← IF selectedD = NIL THEN NIL ELSE selectedD.parts;
SVAssembly.DrawAttractorFeedback[sliceD, selectedParts, dragInProgress, dc, camera];
};
ENDCASE;
};
DrawCpsOfSelectedSlices:
PROC [dc: Imager.Context, scene: Scene, camera: Camera, dragInProgress, caretIsMoving:
BOOL] = {
normalSliceD, hotSliceD: SliceDescriptor;
normalParts, hotParts: SliceParts;
slice: Slice;
IF caretIsMoving OR dragInProgress THEN RETURN;
FOR sList:
LIST
OF Slice ← AllSelectedSlices[scene], sList.rest
UNTIL sList=
NIL
DO
slice ← sList.first;
normalSliceD ← SVSelect.FindSelectedSlice[slice, scene, normal];
hotSliceD ← SVSelect.FindSelectedSlice[slice, scene, hot];
normalParts ← IF normalSliceD # NIL THEN normalSliceD.parts ELSE NIL;
hotParts ← IF hotSliceD # NIL THEN hotSliceD.parts ELSE NIL;
SVAssembly.DrawSelectionFeedback[slice, normalParts, hotParts, dc, camera, dragInProgress, caretIsMoving, FALSE, caretIsMoving];
ENDLOOP;
};
AllSelectedSlices:
PROC [scene: Scene]
RETURNS [selectedList:
LIST
OF Slice ←
NIL] = {
Puts all the selected (hot & normal) slices in a list, and returns them
ptr: LIST OF Slice;
sGen: SliceDescriptorGenerator ← SVSelect.SelectedSlices[scene, hot];
[selectedList, ptr] ← SVUtility.StartSliceList[];
FOR sd: SliceDescriptor ← SVSelect.NextSliceDescriptor[sGen], SVSelect.NextSliceDescriptor[sGen]
UNTIL sd =
NIL
DO
[selectedList, ptr] ← SVUtility.AddSlice[sd.slice, selectedList, ptr];
ENDLOOP;
sGen ← SVSelect.SelectedSlices[scene, normal];
FOR sd: SliceDescriptor ← SVSelect.NextSliceDescriptor[sGen], SVSelect.NextSliceDescriptor[sGen]
UNTIL sd =
NIL
DO
IF NOT SVSelect.IsSelectedInPart[sd.slice, scene, hot] THEN [selectedList, ptr] ← SVUtility.AddSlice[sd.slice, selectedList, ptr];
ENDLOOP;
};
DrawAnySelectionsDCAux:
PROC [dc: Imager.Context, selType: SelectionType, svData: SVData, scene: Scene, camera: Camera] = {
For each selection, If the selected object is a hook or a floater, complement the jack and then draw the selection. If a coordinate system, just draw selection.
g: SelectionGenerator;
selectionsExist: BOOL;
[g, selectionsExist] ← SVSelections.GetSelectionGenerator[selType];
IF NOT selectionsExist THEN RETURN;
FOR sel: Selection ← SVSelections.NextSelection[g], SVSelections.NextSelection[g]
UNTIL sel =
NIL
DO
IF sel.svData = svData
THEN {
DrawSelectionDC[dc, sel, svData];
};
ENDLOOP;
};
DrawSelectionDC:
PROC [dc: Imager.Context, sel: Selection, svData: SVData] = {
DrawSelectionDCAux:
SAFE
PROC = {
Imager.SetColor[dc, Imager.black];
SELECT sel.selectionType FROM
target => {
csCAMERA: Matrix4by4;
csCAMERA ← SVCoordSys.WRTCamera[sel.coincident.coordSys, camera.coordSys];
SVDraw3d.DrawTargetCoordSys[dc, csCAMERA, camera];
};
movee => SVDraw3d.DrawMovee[dc, sel.coincident.coordSys, camera];
plane => {
toolData: ToolData;
planeAssem: Slice;
planeAssem ← sel.coincident;
IF planeAssem.toolMasterObject = NIL OR planeAssem.toolMasterObject.mainBody = NIL THEN ERROR;
toolData ← NARROW[planeAssem.toolMasterObject.mainBody];
SVDraw3d.DrawPlaneSelection[dc, sel.coincident.coordSys, toolData.plane, camera];
};
ENDCASE => ERROR;
};
camera: Camera;
IF svData # sel.svData THEN RETURN;
camera ← svData.camera;
Imager.DoSave[dc, DrawSelectionDCAux];
};
DrawSkitter:
PROC [dc: Imager.Context, editToolData: EditToolData, camera: Camera] = {
skitterWORLD, skitterCAMERA: Matrix4by4;
skitter: Skitter ← editToolData.skitter;
IF NOT SVCaret.Exists[skitter] THEN RETURN;
skitterWORLD ← SVCaret.GetPosition[skitter];
skitterCAMERA ← SVMatrix3d.AInTermsOfB[skitterWORLD, SVCoordSys.WRTWorld[camera.coordSys]];
Imager.SetColor[dc, Imager.black];
SVDraw3d.DrawSkitter[dc, skitterCAMERA, camera];
};
DrawAnchor:
PROC [dc: Imager.Context, svData: SVData, camera: Camera] = {
anchorWORLD, anchorCAMERA: Matrix4by4;
scene: Scene ← svData.scene;
IF NOT SVCaret.Exists[scene.anchor] THEN RETURN;
anchorWORLD ← SVCaret.GetPosition[scene.anchor];
anchorCAMERA ← SVMatrix3d.AInTermsOfB[anchorWORLD, SVCoordSys.WRTWorld[camera.coordSys]];
Imager.SetColor[dc, Imager.black];
SVDraw3d.DrawAnchor[dc, anchorCAMERA, camera];
};
OutsideOf:
PROC [test, bound: SVBoundBox.BoundBox]
RETURNS [
BOOL] = {
RETURN[ test.hiX < bound.loX OR test.loX > bound.hiX OR test.hiY < bound.loY OR test.loY > bound.hiY ]; -- these tests may have to be <= or >=
};
DeeperPlanarPolygon:
PROC [x,y: PriorityQueue.Item, data:
REF ←
NIL]
RETURNS [
BOOL] = {
xS: PlanarSurface ← NARROW[x];
yS: PlanarSurface ← NARROW[y];
RETURN[xS.frontDepth < yS.frontDepth];
};
epsilon: REAL ← 1.0E-1;
DeeperPlanarPolygon: PROC [x,y: PriorityQueue.Item, data: REF ← NIL] RETURNS [BOOL] = {
-- We wish to discover if xs.poly is closer to the eyepoint than ys.poly. Both polys are in camera coordinates.
xS: PlanarSurface ← NARROW[x];
yS: PlanarSurface ← NARROW[y];
camera: Camera ← NARROW[data];
eyePoint: Point3d = [0,0,camera.focalLength];
point: Point3d;
thisDist: REAL;
eyeSideOfX, eyeSideOfY, thisPointSideOfY, thisPointSideOfX, sameSeen, diffSeen: BOOL;
eyeSideOfX ← SVPolygon3d.PointOnNormalSideOfPlane[eyePoint, xS.plane];
eyeSideOfY ← SVPolygon3d.PointOnNormalSideOfPlane[eyePoint, yS.plane];
-- If all xS points are on the camera side of yS THEN xS is closer.
sameSeen ← diffSeen ← FALSE;
FOR i: NAT IN [0..xS.poly.len) DO
point ← xS.poly[i];
thisDist ← SVPolygon3d.SignedPointToPlaneDistance[point, yS.plane];
IF ABS[thisDist] < epsilon THEN LOOP; -- no information from this point
thisPointSideOfY ← thisDist > 0;
IF thisPointSideOfY = eyeSideOfY THEN sameSeen ← TRUE
ELSE diffSeen ← TRUE;
ENDLOOP;
IF sameSeen AND NOT diffSeen THEN RETURN[FALSE]; -- y is better (i.e. deeper)
IF diffSeen AND NOT sameSeen THEN RETURN[TRUE]; -- x is better (i.e. deeper)
-- IF all yS points are on the camera side of xS THEN yS is closer.
sameSeen ← diffSeen ← FALSE;
FOR i: NAT IN [0..yS.poly.len) DO
point ← yS.poly[i];
thisDist ← SVPolygon3d.SignedPointToPlaneDistance[point, xS.plane];
IF ABS[thisDist] < epsilon THEN LOOP; -- no information from this point
thisPointSideOfX ← thisDist > 0;
IF thisPointSideOfX = eyeSideOfX THEN sameSeen ← TRUE
ELSE diffSeen ← TRUE;
ENDLOOP;
IF sameSeen AND NOT diffSeen THEN RETURN[TRUE]; -- x is better (i.e. deeper)
IF diffSeen AND NOT sameSeen THEN RETURN[FALSE]; -- y is better (i.e. deeper)
RETURN[xS.depth < yS.depth];
};
DrawObjectsFiltered:
PROC [dc: Imager.Context, svData: SVData, filter: BoundBox, excludeOverlay:
BOOL ←
FALSE, selectedDepth:
REAL ← -Real.LargestNumber] = {
Paints those objects in the scene within the filter bounding box, and in front of (and including) overObject (in PriorityOrder) into the display context.
DrawObjectsFilteredAux:
PROC = {
-- need to clip to filter, then image
g: AssemblyGenerator ← SVScene.PrimAssembliesInScene[scene];
thisSlice: Slice ← SVScene.NextAssembly[g];
IF overObject # NIL THEN {
UNTIL thisSlice = overObject OR thisSlice = NIL DO thisSlice ← SVScene.NextAssembly[g] ENDLOOP;
IF thisSlice = NIL THEN RETURN;
};
Imager.SetColor[dc, Imager.black];
SVBoundBox.Clip[dc: dc, bBox: filter, screen: camera.screenCS];
SELECT camera.style
FROM
wire, normals => {
FOR slice: Slice ← thisSlice, SVScene.NextAssembly[g]
UNTIL slice =
NIL
DO
IF excludeOverlay AND slice.onOverlay THEN LOOP;
IF NOT SVBoundBox.OutsideOf[SVAssembly.GetBoundBox[slice, NIL, camera], filter] THEN SVAssembly.DrawParts[slice, NIL, dc, scene, camera, FALSE];
ENDLOOP;
};
shaded, hiddenLine => {
predictedBufferSize: NAT ← CountSurfacesInAssembly[scene.assembly];
polygonBuffer: PriorityQueue.Ref ←
PriorityQueue.Predict[predictedBufferSize, DeeperPlanarPolygon, camera];
eyeWorld: Point3d←SVGraphics.LocalToWorld[[0,0,camera.focalLength], camera.coordSys];
FOR slice: Slice ← thisSlice, SVScene.NextAssembly[g]
UNTIL slice =
NIL
DO
IF excludeOverlay AND slice.onOverlay THEN LOOP;
IF NOT SVBoundBox.OutsideOf[SVAssembly.GetBoundBox[slice, NIL, camera], filter] THEN SVAssembly.AddPolygonsToBuffer[polygonBuffer, slice, camera, eyeWorld];
ENDLOOP;
SVAssembly.DrawBuffer[dc, polygonBuffer, scene, camera, camera.style=hiddenLine];
};
ENDCASE => ERROR;
};
scene: Scene ← svData.scene;
camera: Camera ← svData.camera;
IF filter=NIL OR filter.null THEN RETURN;
Imager.DoSaveAll[dc, DrawObjectsFilteredAux];
};
CountSurfacesInAssembly:
PROC [assembly: Slice]
RETURNS [surfCount:
NAT] = {
WITH assembly.shape
SELECT
FROM
assems: SliceList => surfCount ← CountSurfacesInAssemblyList[assems];
shape: Shape => surfCount ← shape.mo.class.countSurf[shape.mo];
ENDCASE => ERROR;
};
CountSurfacesInAssemblyList:
PROC [asl: SliceList]
RETURNS [surfCount:
NAT] = {
surfCount ← 0;
FOR list:
LIST
OF Slice ← asl.list, list.rest
UNTIL list =
NIL
DO
surfCount ← surfCount + CountSurfacesInAssembly[list.first];
ENDLOOP;
};
DrawCPFeedback:
PROC [dc: Imager.Context, svData: SVData, caretIsMoving, dragInProgress:
BOOL] = {
};
SelectionOrCaretChanged:
PROC [screen: Imager.Context, svData: SVData, showColors:
BOOL] = {
The selection plane or the skitter plane is obsolete. Since these planes have no backing bitmaps, no repairs are needed. Simply redraw the planes.
PaintAllPlanes[screen, svData, showColors, FALSE, FALSE];
};
Repairing Various Planes
ObjectChangedInPlace:
PROC [screen: Imager.Context, svData: SVData, selectClass: SelectionClass ← normal, showColors:
BOOL] = {
The selected objects have changed in some small way (e.g. line width or color). Repair the background plane and refresh the screen.
bBox: BoundBox ← SVBoundBox.BoundBoxOfSelected[svData.scene, svData.camera, selectClass];
RepairBackgroundInBoundBox[svData, bBox, TRUE, 0.0, showColors];
PaintAllPlanes[screen, svData, showColors, FALSE, FALSE];
};
DepthOfAssembly:
PROC [slice:
REF
ANY]
RETURNS [depth:
REAL] = {
depth ← -Real.LargestNumber;
};
ObjectAdded:
PROC [screen: Imager.Context, svData: SVData, showColors:
BOOL] = {
An object has been added to the scene. All of the changes are confined to the box svData.refresh.startBoundBox. Repair the background plane and refresh the screen.
assembly: Slice ← NARROW[svData.refresh.addedObject];
bBox: BoundBox ← SVBoundBox.BoundBoxOfAssembly[assembly];
bBox: BoundBox ← NIL;
selectedDepth: REAL ← DepthOfAssembly[svData.refresh.addedObject];
RepairBackgroundInBoundBox[svData, bBox, FALSE, selectedDepth, showColors];
PaintAllPlanes[screen, svData, showColors, FALSE, FALSE];
};
RepairBackgroundInBoundBox:
PROC [svData: SVData, bBox: BoundBox, eraseFirst:
BOOL ←
FALSE, selectedDepth:
REAL, showColors:
BOOL] = {
backgroundContext: Imager.Context ← BufferedRefresh.GetLayerContext[svData.refresh.sandwich, $Background];
IF
NOT showColors
THEN {
PaintObjectsInBox:
PROC = {
IF eraseFirst THEN SVBoundBox.EraseWithinBoundBox[backgroundContext, bBox];
DrawObjectsFiltered[dc: backgroundContext, svData: svData, filter: bBox, selectedDepth: selectedDepth];
};
Imager.DoSaveAll[backgroundContext, PaintObjectsInBox];
BufferedRefresh.SetLayerOK[svData.refresh.sandwich, $Background, TRUE];
};
};
Dynamic
PaintDragOverlay:
PROC [screen: Imager.Context, svData
: SVData, dragInProgress:
BOOL, showColors:
BOOL] = {
This routine is called DuringDrag. Write the overlay shapes, selection feedback, and the foreground shapes onto the chunking bitmap and then dump the chunking bitmap onto the screen.
clientToViewer, viewerToClient: Imager.Transformation;
svData.refresh.dragInProgress ← dragInProgress;
svData.refresh.caretIsMoving ← TRUE;
clientToViewer ← viewerToClient ← ImagerTransformation.Scale[1.0];
BufferedRefresh.DrawSandwich[svData.refresh.sandwich, screen, clientToViewer, viewerToClient, svData, showColors];
};
FinishedAdding:
PROC [screen: Imager.Context, svData: SVData, showColors:
BOOL] = {
The boundBox describes the region of the background which must be updated from the overlay.
selectedDepth: REAL ← BackmostSelectedDepth[svData.scene];
MergeBackgroundAndOverlay[svData, svData.refresh.startBoundBox, FALSE, selectedDepth, showColors];
PaintAllPlanes[screen, svData, showColors, FALSE, FALSE];
};
BackmostSelectedDepth:
PROC [scene: Scene]
RETURNS [selectedDepth:
REAL] = {
g: SVScene.AssemblyGenerator;
sliceD: SliceDescriptor;
g ← SVScene.PrimAssembliesInScene[scene];
FOR slice: Slice ← SVScene.NextAssembly[g], SVScene.NextAssembly[g] UNTIL slice = NIL DO
sliceD ← SVSelect.FindSelectedSlice[slice, scene, normal];
IF sliceD # NIL THEN RETURN[slice];
ENDLOOP;
RETURN[-Real.LargestNumber];
};
FinishedDragging:
PROC [screen: Imager.Context, svData
: SVData, showColors:
BOOL] = {
scene: Scene ← svData.scene;
camera: Camera ← svData.camera;
selectedDepth: REAL ← BackmostSelectedDepth[scene];
GGBoundBox.EnlargeByBox[svData.refresh.startBoundBox, SVBoundBox.BoundBoxOfMoving[scene, camera]];
MergeBackgroundAndOverlay[svData, svData.refresh.startBoundBox, TRUE, selectedDepth, showColors];
PaintAllPlanes[screen, svData, showColors, FALSE, FALSE];
MergeBackgroundAndOverlay:
PROC [svData
: SVData, bBox: BoundBox, eraseFirst:
BOOL ←
FALSE, selectedDepth:
REAL, showColors:
BOOL] = {
The background and overlay planes are about to be recombined into the background plane, after dragging.
MergeBackgroundAndOverlayAux:
PROC = {
IF eraseFirst THEN SVBoundBox.EraseWithinBoundBox[backgroundContext, bBox, camera.screenCS];
DrawObjectsFiltered[dc: backgroundContext, svData: svData, filter: bBox, selectedDepth: selectedDepth];
};
camera: Camera ← svData.camera;
backgroundContext: Imager.Context ← BufferedRefresh.GetLayerContext[svData.refresh.sandwich, $Background];
IF svData.refresh.suppressRefresh THEN RETURN;
IF
NOT showColors
THEN {
Imager.DoSaveAll[backgroundContext, MergeBackgroundAndOverlayAux];
BufferedRefresh.SetLayerOK[svData.refresh.sandwich, $Background, TRUE];
};
};
DuringSelect:
PROC [screen: Imager.Context, svData: SVData, showColors:
BOOL] = {
Similar to PaintAllPlanes
IF svData.refresh.suppressRefresh THEN RETURN;
IF
NOT showColors
THEN {
clientToViewer, viewerToClient: Imager.Transformation;
clientToViewer ← viewerToClient ← ImagerTransformation.Scale[1.0];
BufferedRefresh.DrawSandwich[svData.refresh.sandwich, screen, clientToViewer, viewerToClient, svData, showColors];
}
ELSE {}; -- no feedback in SlowPaint mode
};
SplitBackgroundAndOverlay:
PUBLIC
PROC [svData: SVData, restoreBox: BoundBox] = {
PaintAllButOverlayed:
PROC = {
SVBoundBox.EraseWithinBoundBox[backgroundContext, restoreBox, camera.screenCS];
DrawObjectsFiltered[dc: backgroundContext, svData: svData, filter: restoreBox, excludeOverlay: TRUE, selectedDepth: -Real.LargestNumber];
};
backgroundContext: Imager.Context;
camera: Camera ← svData.camera;
IF svData.refresh.suppressRefresh THEN RETURN;
backgroundContext ← BufferedRefresh.GetLayerContext[svData.refresh.sandwich, $Background];
Imager.DoSaveAll[backgroundContext, PaintAllButOverlayed];
};
Debugging Routines (draw directly on the screen)
DrawColor:
PROC [dc: Imager.Context, svData
: SVData] = {
scene: Scene ← svData.scene;
resolution: REAL;
boundBox: BoundBox;
minX, minY, maxX, maxY: REAL;
camera: Camera ← svData.camera;
tree: CSGTree;
screenMat: Matrix4by4;
aisRope: Rope.ROPE ← Rope.Concat[SVFiles.FilenameMinusExtension[scene.name], ".ais"];
resolution ← camera.resolution;
tree ← SVSceneToTree.AssemblyToTree[scene.assembly, scene, camera];
[minX, minY, maxX, maxY] ← MinAndMaxFromCameraAndTree[camera, tree];
boundBox ← GGBoundBox.CreateBoundBox[minX, minY, maxX, maxY];
screenMat ← SVCoordSys.GetMat[camera.screenCS];
SVImage.DrawAlignedColorImage[dc, aisRope, resolution, [screenMat[1][4], screenMat[2][4]], boundBox, svData.feedback];
};
DrawBlackAndWhite:
PROC [dc: Imager.Context, svData
: SVData] = {
scene: Scene ← svData.scene;
camera: Camera ← svData.camera;
resolution: REAL;
boundBox: BoundBox;
minX, minY, maxX, maxY: REAL;
tree: CSGTree;
screenMat: Matrix4by4;
aisRope: Rope.ROPE ← Rope.Concat[SVFiles.FilenameMinusExtension[scene.name], ".ais"];
resolution ← camera.resolution;
tree ← SVSceneToTree.AssemblyToTree[scene.assembly, scene, camera];
[minX, minY, maxX, maxY] ← MinAndMaxFromCameraAndTree[camera, tree];
boundBox ← GGBoundBox.CreateBoundBox[minX, minY, maxX, maxY];
screenMat ← SVCoordSys.GetMat[camera.screenCS];
SVImage.DrawAlignedBlackAndWhiteImage[dc, aisRope, resolution, [screenMat[1][4], screenMat[2][4]], boundBox, svData.feedback];
Imager.SetColor[dc, Imager.black];
SVGraphics.DrawFrame [dc, camera];
};
MinAndMaxFromCameraAndTree:
PROC [camera: Camera, tree: CSGTree]
RETURNS [minX, minY, maxX, maxY:
REAL] = {
frame: FrameBox ← camera.frame;
boundBox: BoundBox;
defaultHalfSide: REAL = 100.0;
IF frame.fullScreen
THEN {
[boundBox] ← SVPreprocess3d.PreprocessForInteraction[tree, camera];
IF boundBox =
NIL
THEN {
minX ← minY ← -defaultHalfSide;
maxX ← maxY ← defaultHalfSide;
}
ELSE {
minX ← boundBox.loX; minY ← boundBox.loY;
maxX ← boundBox.hiX; maxY ← boundBox.hiY;
};
}
ELSE {
minX ← frame.downLeft[1]; minY ← frame.downLeft[2];
maxX ← frame.upRight[1]; maxY ← frame.upRight[2];
};
};
DrawCoordSystems:
PROC [dc: Imager.Context, svData
: SVData] = {
scene: Scene ← svData.scene;
camera: Camera ← svData.camera;
g: CoordSysGenerator ← SVScene.GetCoordSysGenerator[scene];
mat: Matrix4by4;
FOR cs: CoordSystem ← SVScene.NextCoordSys[g], SVScene.NextCoordSys[g]
UNTIL cs = NIL DO
mat ← SVCoordSys.WRTCamera[cs, camera.coordSys];
Make sure you have up to date coordSys positions.
SVDraw3d.DrawCoordSys[dc, mat, camera];
ENDLOOP;
};
DrawBoundBoxes:
PROC [dc: Imager.Context, svData
: SVData] = {
scene: Scene ← svData.scene;
camera: Camera ← svData.camera;
bBox: BoundBox;
g: SVScene.AssemblyGenerator ← SVScene.PrimAssembliesInScene[scene];
FOR slice: Slice ← SVScene.NextAssembly[g], SVScene.NextAssembly[g]
UNTIL slice =
NIL
DO
bBox ← SVAssembly.GetBoundBox[slice, NIL, camera];
SVBoundBox.DrawBoundBox[dc, bBox, camera.screenCS];
ENDLOOP;
};
DrawBoundSpheres:
PROC [dc: Imager.Context, svData
: SVData] = {
scene: Scene ← svData.scene;
camera: Camera ← svData.camera;
tree: CSGTree;
tree ← SVSceneToTree.AssemblyToTree[scene.assembly, scene, camera]; -- does a TellAboutCameraAndWorld
[] ← SVPreprocess3d.PreprocessForCatScan[tree, camera]; -- computes the bounding spheres
SVDraw3d.DrawBoundSpheres[dc, tree, camera];
};
DrawCrossHairs:
PROC [dc: Imager.Context, svData: SVData] = {
camera: Camera ← svData.camera;
SVDraw3d.Draw2dCoordSys[dc, [0,0], camera];
};
DrawPt:
PROC [dc: Imager.Context, svData: SVData] = {
camera: Camera ← svData.camera;
x, y: REAL;
[x, y] ← GGViewerOps.GetTwoReals[svData.textSection.xyz];
SVDraw3d.DrawX[dc, [x, y], camera];
};
DrawPaint:
PROC [dc: Imager.Context, svData: SVData] = {
OPEN SVMatrix3d;
camera: Camera ← svData.camera;
skitterWORLD, worldCamera: Matrix4by4;
path: ImagerPath.Trajectory;
editToolData: EditToolData ← svData.editToolData;
skitter: Skitter ← editToolData.skitter;
artworkToolData: ArtworkToolData ← NARROW[editToolData.artworkTool.data];
paintColor: Color ← artworkToolData.paintColor;
skitterWORLD ← SVCaret.GetPosition[skitter];
worldCamera ← SVCoordSys.FindWorldInTermsOf[camera.coordSys];
path ← SVGraphics.MoveToAbsolute[Update[Update[[-2, -2, 0], skitterWORLD], worldCamera], camera];
path ← SVGraphics.LineToAbsolute[path, Update[Update[[-2, 2, 0], skitterWORLD], worldCamera], camera];
path ← SVGraphics.LineToAbsolute[path, Update[Update[[2, 2, 0], skitterWORLD], worldCamera], camera];
path ← SVGraphics.LineToAbsolute[path, Update[Update[[2, -2, 0], skitterWORLD], worldCamera], camera];
Imager.SetColor[dc, paintColor];
SVGraphics.DrawStroke[dc, path, 1, TRUE];
};
DrawCatScan: PROC [dc: Imager.Context, svData: SVData] = {
catScan: CatScan ← svData.refresh.catScan;
camera: Camera ← svData.camera;
SVCatScan.DrawSlice3d[dc, catScan, camera];
};
PaintSpot:
PUBLIC
PROC [dc: Imager.Context, svData: SVData] = {
spotPoint: Point2d ← svData.refresh.spotPoint;
camera: Camera ← svData.camera;
SVDraw3d.DrawX[dc, spotPoint, camera];
};
PaintHitLine:
PUBLIC
PROC [dc: Imager.Context, svData: SVData] = {
spotPoint: Point2d ← svData.refresh.spotPoint; -- in Camera
hitPoint: Point3d ← svData.refresh.hitPoint; -- in WORLD
scene: Scene ← svData.scene;
camera: Camera ← svData.camera;
hitPoint ← SVCoordSys.FromCSToCS[hitPoint, scene.coordSysRoot, camera.coordSys];
SVGraphics.SetCPAbsolute[dc, [spotPoint[1], spotPoint[2], 0.0], camera];
SVGraphics.DrawToAbsolute[dc, hitPoint, camera];
};
For Interpress Masters
SnapShot:
PUBLIC
PROC [dc: Imager.Context, svData: SVData] = {
Called by SVMouseEvent.IPSnapShot to get a picture of Solidviews dragging in action.
boundRect: Rectangle ← [x: 0.0, y: 0.0, w: 8.5*72.0, h: 11.0*72.0]; -- room for 8.5 by 11 pictures for now
SnapshotBackground[dc, svData];
RefreshOverlay[dc, boundRect, svData]; -- fortunately, RefreshOverlay doesn't use boundRect
};
SnapshotBackground:
PROC [dc: Imager.Context, svData: SVData] = {
Draw all but the overlay objects into a bitmap. Auxiliary for SnapShot.
scene: Scene ← svData.scene;
camera: Camera ← svData.camera;
sliceGen: AssemblyGenerator;
sliceGen ← SVScene.PrimAssembliesInScene[scene];
RefreshBackground[dc, SVState.GetViewport[svData], svData];
DrawAttractorFeedback[dc, svData, svData.refresh.dragInProgress, svData.refresh.caretIsMoving];
FunctionCache.Flush[svData.refresh.lineCache]; -- need to fully refresh foreground
SVAlign.DrawAlignBagRegardless[dc, svData.hitTest.alignBag, svData];
DrawSkitter[dc, svData.editToolData, camera];
DrawAnchor[dc, svData, camera];
};
InterpressEntireScene:
PUBLIC PROC [dc: Imager.Context, svData: SVData] = {
scene: Scene ← svData.scene;
camera: Camera ← svData.camera;
lo, hi: Point2d;
lo ← [0,0];
hi ← [8.5*72.0, 11.0*72.0];
lo ← SVCoordSys.ScreenToCamera[lo, camera.screenCS];
hi ← SVCoordSys.ScreenToCamera[hi, camera.screenCS];
SVScene.DrawScene[dc, scene, camera];
DrawObjectsFiltered[dc, svData, GGBoundBox.CreateBoundBox[lo[1], lo[2], hi[1], hi[2]] ];
};
The Overlay Plane.
OnOverlay:
PROC [entity:
REF
ANY, svData: SVData]
RETURNS [
BOOL] = {
WITH entity
SELECT
FROM
slice: Slice => RETURN[slice.onOverlay];
sliceD: SliceDescriptor => RETURN[sliceD.slice.onOverlay];
ENDCASE => ERROR;
};
MoveToOverlay:
PUBLIC
PROC [entity:
REF
ANY, svData: SVData] = {
WITH entity
SELECT
FROM
sliceD: SliceDescriptor => {
IF OnOverlay[sliceD, svData] THEN ERROR;
sliceD.slice.onOverlay ← TRUE;
svData.refresh.overlayList ← CONS[sliceD, svData.refresh.overlayList];
};
ENDCASE => ERROR;
svData.refresh.orderedOverlayList ← NIL;
};
MoveToBackground:
PUBLIC
PROC [sliceD: SliceDescriptor, svData: SVData] = {
IF NOT OnOverlay[sliceD, svData] THEN RETURN;
svData.refresh.overlayList ← SVUtility.DeleteSliceDescriptorFromList[sliceD, svData.refresh.overlayList];
sliceD.slice.onOverlay ← FALSE;
svData.refresh.orderedOverlayList ← NIL;
};
MoveAllSelectedToOverlay:
PUBLIC
PROC [svData: SVData, selectClass: SelectionClass] = {
sGen: SliceDescriptorGenerator ← SVSelect.SelectedSlices[svData.scene, selectClass];
FOR sliceD: SliceDescriptor ← SVSelect.NextSliceDescriptor[sGen], SVSelect.NextSliceDescriptor[sGen]
UNTIL sliceD =
NIL
DO
MoveToOverlay[sliceD, svData];
ENDLOOP;
};
MoveOverlayToBackground:
PUBLIC
PROC [svData: SVData] = {
sliceD: SliceDescriptor;
FOR overlayList:
LIST
OF SliceDescriptor ← svData.refresh.overlayList, overlayList.rest
UNTIL overlayList =
NIL
DO
sliceD ← overlayList.first;
sliceD.slice.onOverlay ← FALSE;
ENDLOOP;
svData.refresh.overlayList ← NIL;
svData.refresh.orderedOverlayList ← NIL;
};
EmptyOverlay:
PUBLIC
PROC [svData: SVData]
RETURNS [
BOOL] = {
RETURN[svData.refresh.overlayList = NIL];
};
OrderOverlayList:
PROC [svData: SVData]
RETURNS [orderedList:
LIST
OF SliceDescriptor ←
NIL] = {
traverse the scene.entities from back to end front.
FindOverlayedD:
PROC [slice:
REF
ANY]
RETURNS [sliceD: SliceDescriptor ←
NIL] = {
FOR ov:
LIST
OF SliceDescriptor ← svData.refresh.overlayList, ov.rest
UNTIL ov=
NIL
DO
sliceD ← ov.first;
IF sliceD.slice=slice THEN RETURN[sliceD];
ENDLOOP;
RETURN[NIL];
};
sliceD: SliceDescriptor;
finger: LIST OF SliceDescriptor;
g: SVScene.AssemblyGenerator;
scene: Scene ← svData.scene;
[orderedList, finger] ← SVUtility.StartSliceDescriptorList[];
g ← SVScene.PrimAssembliesInScene[scene];
FOR slice: Slice ← SVScene.NextAssembly[g], SVScene.NextAssembly[g]
UNTIL slice =
NIL
DO
IF OnOverlay[slice, svData]
THEN {
sliceD ← FindOverlayedD[slice];
IF sliceD = NIL THEN sliceD ← SVAssembly.NewParts[slice, NIL, [0,0,0], topLevel];
[orderedList, finger] ← SVUtility.AddSliceDescriptor[sliceD, orderedList, finger];
};
ENDLOOP;
};
InitStats:
PROC = {
interval: CodeTimer.Interval;
interval ← CodeTimer.CreateInterval[$PaintEntireScene];
CodeTimer.AddInt[interval, $Solidviews];
};
InitStats[];
END.