ThreeDDemoImpl.mesa
Last Edited by: Crow, December 8, 1989 11:19:46 pm PST
Bloomenthal, September 26, 1988 12:04:57 pm PDT
DIRECTORY Atom, BasicTime, CedarProcess, Commander, FS, G3dAnimationSupport, G3dBasic, G3dColorDisplaySupport, G3dRender, G3dRenderWithImager, G3dRenderWithPixels, G3dShape, G3dSortandDisplay, Imager, ImagerBackdoor, ImagerPixel, ImageTwiddle, List, Process, Random, Real, Rope, Terminal, ThreeDViewer, ViewerTools;
ThreeDDemoImpl: CEDAR MONITOR
IMPORTS Atom, BasicTime, CedarProcess, Commander, FS, G3dAnimationSupport, G3dColorDisplaySupport, G3dRender, G3dRenderWithImager, G3dRenderWithPixels, G3dShape, G3dSortandDisplay, Imager, ImagerBackdoor, ImagerPixel, ImageTwiddle, List, Process, Random, Real, Rope, Terminal, ThreeDViewer, ViewerTools
~ BEGIN
Types
PropList:   TYPE ~ Atom.PropList;
ROPE:    TYPE ~ Rope.ROPE;
Context:   TYPE ~ G3dRender.Context;
RealSequence: TYPE ~ G3dBasic.RealSequenceRep;
IntegerPair:  TYPE ~ G3dRender.IntegerPair;
Pair:    TYPE ~ G3dRender.Pair;
Pixel:    TYPE ~ G3dRender.Pixel;
Triple:   TYPE ~ G3dRender.Triple;
Rectangle:  TYPE ~ G3dRender.Rectangle;
RGB:    TYPE ~ G3dRender.RGB;
Patch:    TYPE ~ G3dRender.Patch;
ShadingClass: TYPE ~ G3dRender.ShadingClass;
Shape:   TYPE ~ G3dRender.Shape;
ShapeRep:  TYPE ~ G3dShape.ShapeRep;
ShapeSequence: TYPE ~ G3dRender.ShapeSequence;
ShapeSequenceRep: TYPE ~ G3dShape.ShapeSequenceRep;
RenderData:  TYPE ~ G3dRender.RenderData;
ImagerProc:  TYPE ~ G3dRender.ImagerProc;
ImagerProcRec: TYPE ~ G3dRender.ImagerProcRec;
LORA:    TYPE ~ LIST OF REF ANY;
Global Variables
context3d, offScreenCtx: Context;  -- , onScreenCtx
currentScene, scene1, scene2, scene3: ShapeSequence ← NIL;
lastDemoCalled: PROC[makeFrame: BOOLEANFALSE];
frames: REF G3dAnimationSupport.FrameSequence;
xNow, yNow, zNow: REAL ← 3.0;
xPosition, yPosition: REAL ← 0.0;
cursorRange: REAL ← 64.0;
quadTime: REAL;
animation: BOOLEANFALSE;
movieProcess: CedarProcess.Process ← NIL;
cachedViewPort: Rectangle ← [0.0, 0.0, 0.0, 0.0];
windowStack, viewPortStack: LIST OF Rectangle ← NIL;
mouseWaiting: ATOMNIL;
mouseRectangle: REF Rectangle ← NIL;
Renamed Procedures
FindShape: PROC [context: Context, shapeName: ROPE] RETURNS [Shape]
~ G3dRender.FindShape;
GetAIS: PROC[ context: Context, fileRoot: ROPE, xOffset, yOffset: INTEGER ← 0,
     center: BOOLEANTRUE, labeled: BOOLEANFALSE ]
   RETURNS[ xSize, ySize: INTEGER]
~ G3dColorDisplaySupport.GetAIS;
GetProp: PROC [propList: PropList, prop: REF ANY] RETURNS [REF ANY]
~ Atom.GetPropFromList;
PutProp: PROC [propList: PropList, prop: REF ANY, val: REF ANY] RETURNS [PropList]
~ Atom.PutPropOnList;
Menu and Button Procs
demoMenu: LIST OF ThreeDViewer.ButtonDesc ~ LIST [
[ proc: SetScene,            -- Select Scene
choices: LIST[
[$Scene1, "Polyhedra on Checkerboard"],       -- left mouse button
[$Scene2, "Glass, Banana and Egg"],        -- middle mouse button
[$Scene3, "Bezier Teapot"],          -- right mouse button
[$OneShape, "Show One Shape (name from selection)"],  -- shift left
[NIL, NIL],               -- shift middle
[NIL, NIL],               -- shift right
[$Window, "2 Clicks in image define part of scene to display"],   -- control left
[$ViewPort, "2 Clicks in image define new screen area"],    -- control middle
[NIL, NIL],                 -- control right
[$RestoreW, "Restore previous window"],       -- control shift left
[$RestoreVP, "Restore previous viewport"]       -- control shift middle
],
label: "Select Shapes",
purpose: "Chooses scene to manipulate"
],
[ proc: SetShading,           -- Shading Style
choices: LIST[
[$Lines, "Line Drawing"],        -- left mouse button
[$Facets, "Faceted Shading"],        -- middle mouse button
[$Smooth, "Smooth Shading"],       -- right mouse button
[$Hidden, "Hidden Line Drawing"],     -- shift left
[$Shiny, "Faceted Shading with Highlights"],   -- shift middle
[$Hilights, "Smooth Shading with Highlights"],  -- shift right
[$SmoLines, "Smooth Shading on Lines"],    -- control left
[$Normals, "Line drawing with normal vectors"]  -- control middle
],
label: "Shading Style",
purpose: "Chooses shading for surfaces in current scene"
],
[ proc: SetRenderingStyle,         -- Rendering Style
choices: LIST[
[$Jaggy, "Aliased"],           -- left mouse button
[$NoJaggy, "AntiAliased"],        -- middle mouse button
[NIL, NIL],             -- right mouse button
[$Visible, "Build Image On Display"],      -- shift left
[$Buffered, "Double Buffer Image"],       -- shift middle
[NIL, NIL],             -- shift right
[$Imager, "Use Imager (faceted only)"],      -- control left
[$Pixels, "Use fancier tiler/shaders"],      -- control middle
[NIL, NIL],             -- control right
[$ZBuffer, "Use depth buffer for hidden surfaces"],  -- control shift left
[$Sorted, "Use sorting for hidden surfaces"],    -- control shift middle
[NIL, NIL],             -- control shift right
[$PseudoClr, "8-bit mapped color"],
[$Gray, "8-bit grayscale"],
[$FullClr, "24-bit full color"]
],
label: "Rendering Style",
purpose: "Chooses display type, antialising, and double buffering"
],
[ proc: DoCommands,           -- Commands
choices: LIST[
[$Display, "Make a frame with the current scene"],    -- left mouse button
[$StoreImage, "Store image in file (name from selection)"],  -- middle mouse button
[$Interpress, "Interpress file (name from selection)"]    -- right mouse button
],
label: "Do It",
purpose: "Make/Store images"
],
[ proc: ShowMotion,           -- Animation
choices: LIST[
[$Orbit, "Orbit about scene computing frames on the fly"],  -- left mouse button
[$SelectDthr, "Take 8-bit dithered sequence name from selection"], -- middle button
[$SelectGray, "Take 8-bit grayscale sequence name from selection"], -- right button
[$Shapes, "Play back Banana, Glass, Egg scene"],     -- shift left
[$Terrain, "Play back Terrain flyover"],       -- shift middle
[NIL, NIL],               -- shift right
[$Web1, "Play back front view Paper web"],     -- control left
[$Web2, "Play back top view Paper web"],       -- control middle
],
label: "Animation",
purpose: "Chooses various animations"
],
[ proc: ShowPicture,           -- Gallery
choices: LIST[ 
[$SelectColor, "Take rgb file name from selection"],     -- left mouse button
[$SelectDthr, "Take 8-bit dithered file name from selection"], -- middle mouse button
[$SelectGray, "Take 8-bit grayscale file name from selection"], -- right mouse button
[$Spoon, "Newell's Bezier patch spoon"],    -- shift left
[$Bananas, "A field of bananas"],      -- shift middle
[$Compare, "Shading Comparison on Eggs"],   -- shift right
[$Bowl, "Sugar bowl with spoon"],      -- control left
[$TeaPot, "Teapot with Perlin texture"],     -- control middle
[$Eggtxtr, "Multi-textured Egg"],      -- control right
[$Heart, "Heart from two Bezier patches"],    -- control shift left
[$Objects, "Five objects on a checkerboard"],   -- control shift middle
[$Extents, "Five objects with bounding boxes"],  -- control shift right
[$HexTiles, "Objects on hex-tiled plane"],
[$MikHead, "Mik leers over the curvaceous glass"],
[$MikTrees, "Mik leers over scanned-in trees"],
[$BallPage, "A sphere with a page of text"],
[$BallPages, "A sphere with many pages of text"],
[$TeaPages, "Teapot with pages of text"],
[$SolidLines, "Drawing with fat lines"]
],
label: "Gallery",
purpose: "Chooses a pretty picture to display"
],
[ proc: StopEverything,          -- STOP! button
choices: LIST[],      -- No Choices, just calls proc
label: "STOP!",
purpose: "Panic button, stops all 3D activity"
],
[ proc: DoReset,            -- Reset button
choices: LIST[],      -- No Choices, just calls proc
label: "Reset",
purpose: "Reset button, can often get things unwedged"
]
];
SetScene: ENTRY PROC[context: Context, key: ATOM] ~ {
context.stopMe^ ← FALSE;      -- unstick stop button
{ ENABLE UNWIND => NULL; -- get out and release lock if error, etc.
SELECT key FROM
$Scene1 => SetScene1[];
$Scene2 => SetScene2[];
$Scene3 => SetScene3[];
$OneShape => OneShape[];
$Window => GetWindow[];
$ViewPort => GetViewPort[];
$RestoreW => RestoreWindow[];
$RestoreVP => RestoreViewPort[];
ENDCASE;
};
};
SetShading: ENTRY PROC[context: Context, key: ATOM] ~ {
context.stopMe^ ← FALSE;      -- unstick stop button
{ ENABLE UNWIND => NULL;
SELECT key FROM
$Lines => LinesDemo[];
$Facets => { SetDull[]; FacetedDemo[]; };
$Smooth => { SetDull[]; SmoothDemo[]; };
$Hidden => HiddenLinesDemo[];
$Shiny => { SetShiny[]; FacetedDemo[]; };
$Hilights => { SetShiny[]; SmoothDemo[]; };
$SmoLines => IF context.class.displayType # $PseudoColor
OR ThreeDViewer.SwitchDisplayTo[context, $FullColor]
THEN {       -- only for $Gray and $FullColor
context.preferredRenderMode ← $Pixels;
ShadedLinesDemo[];
};
$Normals => NormaledLinesDemo[];
ENDCASE;
};
};
SetRenderingStyle: ENTRY PROC[context: Context, key: ATOM] ~ {
context.stopMe^ ← FALSE;      -- unstick stop button
SELECT key FROM
$Jaggy  => G3dRenderWithPixels.AntiAliasing[context, FALSE];
$Visible  => G3dRenderWithPixels.BufferRendering[context, FALSE];
$Imager  => context.preferredRenderMode ← $Imager;
$NoJaggy  => IF context.class.displayType # $PseudoColor
OR ThreeDViewer.SwitchDisplayTo[context, $FullColor]
THEN {
context.preferredRenderMode ← $Pixels;
G3dRenderWithPixels.AntiAliasing[context, TRUE];
};
$Buffered => G3dRenderWithPixels.BufferRendering[context, TRUE];
$Pixels  => context.preferredRenderMode ← $Pixels;
$ZBuffer => G3dRenderWithPixels.DepthBuffering[context, TRUE];
$Sorted  => G3dRenderWithPixels.DepthBuffering[context, FALSE];
$PseudoClr => [] ← ThreeDViewer.SwitchDisplayTo[context, $PseudoColor];
$Gray   => [] ← ThreeDViewer.SwitchDisplayTo[context, $Gray];
$FullClr  => [] ← ThreeDViewer.SwitchDisplayTo[context, $FullColor];
ENDCASE;
{ ENABLE UNWIND => NULL;
ThreeDViewer.DrawInViewer[context, NIL];
};
};
DoCommands: ENTRY PROC[context: Context, key: ATOM] ~ {
context.stopMe^ ← FALSE;      -- unstick stop button
{ ENABLE UNWIND => NULL;
SELECT key FROM
$Display => {
IF DemoErrorMessages[] THEN RETURN;
SELECT lastDemoCalled FROM
LinesDemo   => MakeFrame[];
ShadedLinesDemo => MakeFrame[];
HiddenLinesDemo => HiddenLinesDemo[TRUE];
NormaledLinesDemo => NormaledLinesDemo[TRUE];
FacetedDemo => MakeFrame[];
SmoothDemo => MakeFrame[];
ENDCASE  => MakeFrame[];
};
$StoreImage => G3dColorDisplaySupport.PutAIS[
           context3d, ViewerTools.GetSelectionContents[] ];
$Interpress => InterpressDemo[];
ENDCASE;
};
};
ShowMotion: ENTRY PROC[context: Context, key: ATOM] ~ {
SetSingleBuffer: PROC[] ~ {
G3dRenderWithPixels.AntiAliasing[context, FALSE];
G3dRenderWithPixels.DepthBuffering[context, FALSE];
};
context.stopMe^ ← FALSE;      -- unstick stop button
{ ENABLE UNWIND => NULL;
IF key = $Orbit OR context.class.displayType # $FullColor
OR ThreeDViewer.SwitchDisplayTo[context, $PseudoColor] -- ensure 8-bit display
THEN SELECT key FROM
$Orbit => Orbit[];
$SelectDthr => {
[] ← ThreeDViewer.SwitchDisplayTo[context, $PseudoColor]; SetSingleBuffer[];
SetUpMovie[ fileRoot: ViewerTools.GetSelectionContents[] ];
};
$SelectGray => {
[] ← ThreeDViewer.SwitchDisplayTo[context, $Gray]; SetSingleBuffer[];
SetUpMovie[ fileRoot: ViewerTools.GetSelectionContents[] ];
};
$Shapes => {
[] ← ThreeDViewer.SwitchDisplayTo[context, $PseudoColor]; SetSingleBuffer[];
SetUpMovie[
fileRoot: "/Pixel/Crow/Animation/StillCloser.ais", numFiles: 40, start: 1
];
};
$Terrain => {
[] ← ThreeDViewer.SwitchDisplayTo[context, $PseudoColor]; SetSingleBuffer[];
SetUpMovie[ fileRoot: "/Cyan/AIS/Animation/LakeSceneTest.ais", numFiles: 64 ];
ImageTwiddle.SetUpTerrainColors[context.terminal];
};
$Web1 => {
[] ← ThreeDViewer.SwitchDisplayTo[context, $Gray]; SetSingleBuffer[];
SetUpMovie[ fileRoot: "/Pixel/Crow/Animation/Paper4Web.ais", numFiles: 40 ];
};
$Web2 => {
[] ← ThreeDViewer.SwitchDisplayTo[context, $Gray]; SetSingleBuffer[];
SetUpMovie[ fileRoot: "/Pixel/Crow/Animation/Paper5Web.ais", numFiles: 40 ];
};
ENDCASE;
IF key # $Orbit THEN movieProcess ← CedarProcess.Fork[PlayBackbyFrame];
};
};
ShowPicture: ENTRY PROC[context: Context, key: ATOM] ~ {
context.stopMe^ ← FALSE;      -- unstick stop button
{ ENABLE UNWIND => NULL;
SELECT key FROM
$SelectDthr => IF context.class.displayType # $PseudoColor THEN
IF ThreeDViewer.SwitchDisplayTo[context, $PseudoColor] THEN -- switch to dithered
{ [] ← CedarProcess.Fork[ WaitThenShowPicture, LIST[context, key] ]; RETURN; };
$SelectGray => IF context.class.displayType # $Gray THEN
IF ThreeDViewer.SwitchDisplayTo[context, $Gray] THEN -- switch to 8-bit gray
{ [] ← CedarProcess.Fork[ WaitThenShowPicture, LIST[context, key] ]; RETURN; };
ENDCASE => IF context.class.displayType # $FullColor THEN
IF ThreeDViewer.SwitchDisplayTo[context, $FullColor] THEN-- switch to full color
{ [] ← CedarProcess.Fork[ WaitThenShowPicture, LIST[context, key] ]; RETURN; };
SELECT key FROM
$SelectDthr, $SelectGray, $SelectColor =>
     [] ← GetAIS[context3d, ViewerTools.GetSelectionContents[] ];
$Spoon  => [] ← GetAIS[context3d, "/cyan/AIS/Crow/Spoon.ais" ];
$Bananas  => [] ← GetAIS[context3d, "/cyan/AIS/Crow/FieldOfBanana.ais" ];
$Compare  => [] ← GetAIS[context3d, "/cyan/AIS/Crow/ComparisonEggs.ais" ]; 
$Bowl   => [] ← GetAIS[context3d, "/cyan/AIS/Crow/BowlAndSpoon.ais" ];
$TeaPot   => [] ← GetAIS[context3d, "/cyan/AIS/Crow/ZebraBurlTeaPot.ais" ];
$Eggtxtr  => [] ← GetAIS[context3d, "/cyan/AIS/Crow/MultiTxtrdEgg2.ais" ];
$Heart  => [] ← GetAIS[context3d, "/cyan/AIS/Crow/Heart.ais" ];
$Objects  => [] ← GetAIS[context3d, "/cyan/AIS/Crow/TestScene2.ais" ];
$Extents  => [] ← GetAIS[context3d, "/cyan/AIS/Crow/FiveObjWBoxes.ais" ];
$HexTiles  => [] ← GetAIS[context3d, "/cyan/AIS/Crow/HexTiles.ais" ];
$MikHead => [] ← GetAIS[context3d, "/cyan/AIS/Crow/Lamming6Scene.ais" ];
$MikTrees => [] ← GetAIS[context3d, "/cyan/AIS/Crow/Lamming7Scene.ais" ];
$BallPage  => [] ← GetAIS[context3d, "/cyan/AIS/Crow/SphereWithPage.ais" ];
$BallPages  => [] ← GetAIS[context3d, "/cyan/AIS/Crow/SphereWithPages.ais" ];
$TeaPages => [] ← GetAIS[context3d, "/cyan/AIS/Crow/PagedTeaPot.ais" ];
$SolidLines => [] ← GetAIS[context3d, "/cyan/AIS/Crow/StillLines.ais" ];
ENDCASE
};
};
WaitThenShowPicture: CedarProcess.ForkableProc ~ {
PROC [data: REF] RETURNS [results: REFNIL];
Forking this process frees button, reducing likelihood of deadlocks, at least I think that's why this is done this way
list: LORANARROW[data];
context: ContextNARROW[list.first];
key: ATOMNARROW[list.rest.first];
WHILE GetProp[ context.displayProps, $ViewerAdjusted ] # NIL DO
Process.PauseMsec[ 500 ]; ENDLOOP;    -- wait for viewer paint proc to complete
ShowPicture[context, key];
};
StopEverything: PROC[context: Context, key: ATOM] ~ {    -- panic stop
bkgrdContext: ContextNIL;
WITH GetProp[context.props, $BackGround] SELECT FROM
bkGrdCtx: Context => bkgrdContext ← bkGrdCtx;
ENDCASE;
context.stopMe^ ← TRUE;
WHILE bkgrdContext # NIL DO    -- stop chain of background contexts
tmpCtx: Context ← bkgrdContext;
tmpCtx.stopMe^ ← TRUE;
bkgrdContext ← NARROW[GetProp[tmpCtx.props, $BackGround]];
ENDLOOP;
IF movieProcess # NIL THEN {
Process.Yield[];
CedarProcess.Abort[movieProcess];
movieProcess ← NIL;
Process.Yield[];
G3dRender.SetViewPort[ context, cachedViewPort ];
};
};
DoReset: PROC [context: Context, key: ATOM] ~ { -- get out of stuckness, start over
StopEverything[context, NIL];
Process.PauseMsec[ 3000 ];       -- wait 3 seconds for processes to stop
context3d.stopMe^ ← FALSE;       -- get stop flag unstuck, if necessary
context3d.window ← NIL; 
context3d.shapes ← context3d.visibleShapes ← NIL; 
context3d.lightSources ← NIL;
G3dRender.SetView[ context3d, [2.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
G3dRender.AddLight[context3d, "Light0", [-100., -200., 50.] ];
G3dRender.SetViewPort[context3d, [0.0, 0.0, 65536.0, 65536.0] ];
windowStack ← viewPortStack ← NIL;
mouseWaiting ← NIL;
mouseRectangle ← NIL;
currentScene ← NIL;
frames ← NIL;
context3d.props ← Atom.RemPropFromList[ context3d.props, $SinglePatch ];
context3d.props ← Atom.RemPropFromList[ context3d.props, $SingleVtx ];
};
Context and Interactive Control
ThreeDDemoCommand: Commander.CommandProc ~ {
context3d ← G3dRender.Create[];
context3d.preferredRenderMode ← $Pixels;  -- $Pixels, Imager;
ThreeDViewer.MakeViewer[
context: context3d,
displayType: $PseudoColor,    -- $Gray, ImagerDithered
bannerName: "ThreeDWorld Demonstration",
menu: demoMenu,
mouseAction: MouseControl,
verticalMenu: FALSE
];
G3dRender.NameBackgroundColor[ context3d, "Darkish Blue" ]; -- set background color
G3dRender.NameAmbientLight[ context3d, "Darkish Gray" ]; -- set ambient light
G3dRender.AddLight[context3d, "Light0", [-100., -200., 50.] ];
G3dRender.SetView[ context3d, [2.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
};
InitNoDisplay: PROC[] ~ {
context3d ← G3dRender.Create[];
G3dRender.NameBackgroundColor[ context3d, "Darkish Blue" ]; -- set background color
G3dRender.NameAmbientLight[ context3d, "Darkish Gray" ]; -- set ambient light
G3dRender.AddLight[context3d, "Light0", [-100., -200., 50.] ];
G3dRender.SetView[ context3d, [2.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
};
InitNoViewer: PROC[] ~ {
context3d ← G3dRender.Create[];
G3dRender.LoadDisplayClass[context3d, $PseudoColor];
G3dRender.NameBackgroundColor[ context3d, "Darkish Blue" ]; -- set background color
G3dRender.NameAmbientLight[ context3d, "Darkish Gray" ]; -- set ambient light
G3dRender.AddLight[context3d, "Light0", [-100., -200., 50.] ];
G3dRender.SetView[ context3d, [2.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
};
InitNoViewerFullColor: PROC[] ~ {
context3d ← G3dRender.Create[];
G3dRender.LoadDisplayClass[context3d, $FullColor];
G3dRender.NameBackgroundColor[ context3d, "Darkish Blue" ]; -- set background color
G3dRender.NameAmbientLight[ context3d, "Darkish Gray" ]; -- set ambient light
G3dRender.AddLight[context3d, "Light0", [-100., -200., 50.] ];
G3dRender.SetView[ context3d, [2.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
};
Reset: PROCEDURE [] ~ {
DoReset[context3d, NIL];
};
MouseControl: ThreeDViewer.MouseProc ~ {
PROC[context: Context, bttn, ctlShft: ATOM, x, y: REAL]
bttn in { $LeftButton, $LeftHeld, $MiddleButton, $MiddleHeld, $RightButton, $RightHeld }
ctlShft in { NIL, $Shift, $Control, $ControlShift }
This will be called whenever the mouse is moved or a mouse button is pushed.
width, height: INTEGER;
divisor: REAL;
IF mouseWaiting # NIL
THEN { IF bttn = $LeftButton THEN GetMouseRectangle[x, y]; RETURN; };
IF context.viewer # NIL
THEN { width ← context.viewer.ww; height ← context.viewer.wh; }
ELSE { width ← context.pixels.box.max.f - context.pixels.box.min.f;
   height ← context.pixels.box.max.s - context.pixels.box.min.s; };
divisor ← ((width/2.0) * (width/2.0)) / cursorRange;
x ← x - width / 2.0;    -- center mouse coords
y ← y - height / 2.0;
SELECT bttn FROM
$LeftButton, $LeftHeld => IF movieProcess # NIL
THEN frameRate ← INTEGER[Real.Fix[x / 10.]]
ELSE {
xNow ← (x * ABS[x] / divisor) + context3d.lookAt.x;
yNow ← (y * ABS[y] / divisor) + context3d.lookAt.y;
G3dRender.SetView[ context, [xNow, yNow, zNow], context.lookAt, context.fieldOfView, context.rollAngle, context.upDirection, context.hitherLimit, context.yonLimit ];
DoCommands[context, $Display];
};
$MiddleButton, $MiddleHeld => {
xLight: REAL ← x * 1.0;   -- roughly plus or minus 300-500
yLight: REAL ← y * 1.0;
[] ← G3dRender.AddLight[ context, "Light0", [xLight, yLight, 50.0] ];
DoCommands[context, $Display];
};
$RightButton, $RightHeld => {
zNow ← (y * ABS[y] / divisor) + context3d.lookAt.z;
G3dRender.SetView[ context, [xNow, yNow, zNow], context.lookAt, context.fieldOfView, context.rollAngle, context.upDirection, context.hitherLimit, context.yonLimit ];
DoCommands[context, $Display];
};
ENDCASE;
};
MoveToCursor: PROC[] ~ {
width, height, offset: INTEGER;
sPos: IntegerPair;
IF context3d.viewer # NIL
THEN {
width ← context3d.viewer.ww; height ← context3d.viewer.wh; offset ← 28;
}
ELSE {
width ← context3d.pixels.box.max.f - context3d.pixels.box.min.f; 
height ← context3d.pixels.box.max.s - context3d.pixels.box.min.s;
offset ← context3d.pixels.box.min.s
};
[[sPos.x, sPos.y]] ← Terminal.GetColorCursorPosition[context3d.terminal];
xPosition ← 1.0 * sPos.x / width;
yPosition ← 1.0 * (height - sPos.y + offset) / height;
};
DrawToCursor: PROC[ color: RGB ← [1.0,1.0,1.0] ] ~ {
width, height, offset: INTEGER;
sPos: IntegerPair;
newPos: Pair;
clrPxl: Pixel ← [Real.Fix[color.R*255], Real.Fix[color.G*255], Real.Fix[color.B*255], 0, 0];
IF context3d.viewer # NIL
THEN {
width ← context3d.viewer.ww; height ← context3d.viewer.wh; offset ← 28;
}
ELSE {
width ← context3d.pixels.box.max.f - context3d.pixels.box.min.f; 
height ← context3d.pixels.box.max.s - context3d.pixels.box.min.s;
offset ← context3d.pixels.box.min.s
};
[[sPos.x, sPos.y]] ← Terminal.GetColorCursorPosition[context3d.terminal];
newPos ← [1.0 * sPos.x / width, 1.0 * (height - sPos.y + offset) / height];
context3d.class.draw2DLine[ context3d, [xPosition, yPosition], newPos, clrPxl ];
xPosition ← newPos.x;
yPosition ← newPos.y;
};
TextAtCursor: PROC[ rope: ROPE, color: RGB ← [1.0,1.0,1.0], size: REAL ← 20.0,
       font: ROPENIL ] ~ {
sPos: IntegerPair;
pos: Pair;
width, height, offset: INTEGER;
clrPxl: Pixel ← [Real.Fix[color.R*255], Real.Fix[color.G*255], Real.Fix[color.B*255], 0, 0];
IF context3d.viewer # NIL
THEN {
width ← context3d.viewer.ww; height ← context3d.viewer.wh; offset ← 28;
}
ELSE {
width ← context3d.pixels.box.max.f - context3d.pixels.box.min.f; 
height ← context3d.pixels.box.max.s - context3d.pixels.box.min.s;
offset ← context3d.pixels.box.min.s
};
[[sPos.x, sPos.y]] ← Terminal.GetColorCursorPosition[context3d.terminal];
pos ← [1.0 * sPos.x / width, 1.0 * (height - sPos.y + offset) / height];
IF font = NIL THEN font ← "Xerox/Pressfonts/TimesRoman-MRR";
context3d.class.draw2DRope[context3d, rope, pos, clrPxl, size, font];
};
GetOffScreenCtx: PROC[context: Context, width, height: NAT] ~ {
offScreenCtx ← G3dRender.GetTmpContext[context];
offScreenCtx.viewer ← NIL;
offScreenCtx.viewPort ← NEW[     -- set viewport directly to define pixelMap size
Rectangle ← [ x: 0.0, y: 0.0, w: Real.Float[width], h: Real.Float[height] ]
];
G3dRenderWithPixels.AllocatePixelMemory[offScreenCtx];     -- get display memory
G3dRender.SetViewPort[ offScreenCtx, [0.0, 0.0, Real.Float[width], Real.Float[height]] ];
IF context.antiAliasing THEN G3dRenderWithPixels.AntiAliasing[offScreenCtx];-- get state straight
IF context.depthBuffering THEN G3dRenderWithPixels.DepthBuffering[offScreenCtx];
[] ← G3dRender.StartLog[offScreenCtx];
offScreenCtx.changed ← TRUE;
G3dSortandDisplay.ValidateContext[offScreenCtx];
G3dSortandDisplay.ValidateContext[context];   -- just in case it hasn't been used yet
};
GetOnScreenCtx: PROC[context: Context, width, height: NAT] ~ {
Makes a new view of the supplied context, useful for using two portions of the screen differently, etc.
onScreenCtx: Context ← G3dRender.GetTmpContext[context];
G3dRender.SetViewPort[ onScreenCtx, [0.0, 0.0, Real.Float[width], Real.Float[height]] ];
onScreenCtx.class.render ← G3dRenderWithPixels.MakeFrame;  -- load direct memory procs
onScreenCtx.class.displayPolygon ← G3dRenderWithPixels.PolygonTiler;
IF context.antiAliasing THEN G3dRenderWithPixels.AntiAliasing[onScreenCtx];
IF context.depthBuffering THEN G3dRenderWithPixels.DepthBuffering[onScreenCtx];
onScreenCtx.changed ← TRUE;
G3dSortandDisplay.ValidateContext[onScreenCtx];
G3dSortandDisplay.ValidateContext[context];   -- just in case it hasn't been used yet
};
Demos
DemoErrorMessages: PROCEDURE [] RETURNS[ error: BOOLEANFALSE ] ~ {
NoSurfaces: PROCEDURE [shapes: ShapeSequence] RETURNS[BOOLEAN]~{
FOR i: NAT IN [0..shapes.length) DO
IF shapes[i].vertices # NIL AND shapes[i].surfaces # NIL THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE];
};
IF context3d.shapes = NIL OR NoSurfaces[context3d.shapes] THEN SetScene1[];
IF context3d.shapes = NIL OR NoSurfaces[context3d.shapes] THEN {
context3d.class.draw2DRope[context3d, "No objects, nothing to display", [60, 340] ];
error ← TRUE;
};
IF context3d.antiAliasing
AND NOT List.Memb[ context3d.class.displayType, LIST[$Gray, $FullColor] ]
THEN {
context3d.class.draw2DRope[context3d,
        "NoJaggy needs GrayScale or 24-bit Color", [60, 300] ];
error ← TRUE;
};
};
LinesDemo: PROCEDURE [makeFrame: BOOLEANFALSE] ~ {
IF DemoErrorMessages[] THEN RETURN;
FOR i: NAT IN [0..context3d.shapes.length) DO
G3dRender.SetRenderStyle[ FindShape[context3d, context3d.shapes[i].name], lines ];
ENDLOOP;
context3d.changed ← TRUE;
IF makeFrame THEN context3d.class.render[context3d];
lastDemoCalled ← LinesDemo;
};
ShadedLinesDemo: PROCEDURE [makeFrame: BOOLEANFALSE] ~ {
IF DemoErrorMessages[] THEN RETURN;
FOR i: NAT IN [0..context3d.shapes.length) DO
G3dRender.SetRenderStyle[ FindShape[context3d, context3d.shapes[i].name], shadedLines ];
ENDLOOP;
context3d.changed ← TRUE;
IF makeFrame THEN context3d.class.render[context3d];
lastDemoCalled ← ShadedLinesDemo;
};
HiddenLinesDemo: PROCEDURE [makeFrame: BOOLEANFALSE] ~ {
tmpContext: Context ← G3dRender.GetTmpContext[context3d]; -- get modifiable context
IF DemoErrorMessages[] THEN RETURN;
G3dRender.NameBackgroundColor[ tmpContext, "White" ];
G3dRender.CopyContextShapes[tmpContext, context3d]; -- insulate shapes from changes
FOR i: NAT IN [0..tmpContext.shapes.length) DO
G3dRender.SetRenderStyle[
FindShape[tmpContext, tmpContext.shapes[i].name], hiddenLines
];
ENDLOOP;
context3d.changed ← TRUE;
IF makeFrame THEN tmpContext.class.render[tmpContext];
lastDemoCalled ← HiddenLinesDemo;
};
NormaledLinesDemo: PROCEDURE [makeFrame: BOOLEANFALSE] ~ {
tmpContext: Context ← G3dRender.GetTmpContext[context3d]; -- get modifiable context
IF DemoErrorMessages[] THEN RETURN;
G3dRender.NameBackgroundColor[ tmpContext, "White" ];
G3dRender.CopyContextShapes[tmpContext, context3d]; -- insulate shapes from changes
FOR i: NAT IN [0..tmpContext.shapes.length) DO
G3dRender.SetColor[ FindShape[tmpContext, tmpContext.shapes[i].name], [1.0, 1.0, 1.0] ];
G3dRender.SetRenderStyle[
FindShape[tmpContext, tmpContext.shapes[i].name], hiddenLines
];
ENDLOOP;
context3d.changed ← TRUE;
IF makeFrame THEN tmpContext.class.render[tmpContext];
lastDemoCalled ← NormaledLinesDemo;
};
FacetedDemo: PROCEDURE [makeFrame: BOOLEANFALSE] ~ {
IF DemoErrorMessages[] THEN RETURN;
FOR i: NAT IN [0..context3d.shapes.length) DO
G3dRender.SetRenderStyle[ FindShape[context3d, context3d.shapes[i].name], faceted ];
ENDLOOP;
context3d.changed ← TRUE;
IF makeFrame THEN context3d.class.render[context3d];
lastDemoCalled ← FacetedDemo;
};
SmoothDemo: PROCEDURE [makeFrame: BOOLEANFALSE] ~ {
IF DemoErrorMessages[] THEN RETURN;
FOR i: NAT IN [0..context3d.shapes.length) DO
G3dRender.SetRenderStyle[ FindShape[context3d, context3d.shapes[i].name], smooth ];
ENDLOOP;
context3d.changed ← TRUE;
lastDemoCalled ← SmoothDemo;
IF makeFrame THEN context3d.class.render[context3d];
};
SetShiny: PROCEDURE [] ~ {
FOR i: NAT IN [0..context3d.shapes.length) DO
G3dRender.SetShininess[ FindShape[context3d, context3d.shapes[i].name], 50.0 ];
ENDLOOP;
context3d.changed ← TRUE;
};
SetDull: PROCEDURE [] ~ {
Remove Highlights
FOR i: NAT IN [0..context3d.shapes.length) DO
G3dRender.SetShininess[ FindShape[context3d, context3d.shapes[i].name], 0.0 ];
ENDLOOP;
context3d.changed ← TRUE;
};
InterpressDemo: PROCEDURE [] ~ {
fileName: Rope.ROPE ← ViewerTools.GetSelectionContents[];
IF Rope.Length[fileName] < 1 THEN {
SIGNAL G3dRender.Error[$MisMatch, "No selection for file name"];
RETURN;
};
IF DemoErrorMessages[] THEN RETURN;
IF lastDemoCalled # HiddenLinesDemo AND lastDemoCalled # NormaledLinesDemo
THEN
G3dRenderWithImager.MakeInterpressPage[context3d, fileName ]
ELSE {
tmpContext: Context ← G3dRender.GetTmpContext[context3d]; -- get mod. context
context3d.changed ← TRUE;
G3dRender.NameBackgroundColor[ tmpContext, "White" ];
G3dRender.CopyContextShapes[tmpContext, context3d]; -- insulate shapes from change
FOR i: NAT IN [0..context3d.shapes.length) DO
G3dRender.SetColor[ FindShape[tmpContext, tmpContext.shapes[i].name], [1.0,1.0,1.0] ];
IF G3dRender.ShapeClassFrom[tmpContext.shapes[i]].type # $Light  -- avoid lights
THEN IF lastDemoCalled # NormaledLinesDemo
THEN G3dRender.SetRenderStyle[
   FindShape[tmpContext, tmpContext.shapes[i].name], hiddenLines ]
ELSE G3dRender.SetRenderStyle[
   FindShape[tmpContext, tmpContext.shapes[i].name], linesWnormals ];
ENDLOOP;
G3dRenderWithImager.MakeInterpressPage[tmpContext, fileName ]
};
};
RandomQuads: PROCEDURE [context: Context] ~ {
x, y: REAL;
time: REAL ← BasicTime.PulsesToSeconds[BasicTime.GetClockPulses[]];
width: REAL ← context.viewPort.w; height: REAL ← context.viewPort.h;
aspectRatio: REAL ← height / width;
rs: Random.RandomStream ← Random.Create[Real.Round[width]]; -- limit to display width
p: REF Patch ← NEW[ Patch[4] ];
shape: Shape ← NEW[ShapeRep];
renderData: REF RenderData ← G3dRender.RenderDataFrom[shape];
renderData.shadingClass ← NEW[ShadingClass];
renderData.shadingClass.renderMethod ← $Smooth;
p.props ← PutProp[ p.props, $Shape, shape ];
p.nVtces ← 4;
FOR i: NAT IN [0..1000) DO
IF context.stopMe^ THEN EXIT;
x ← Random.NextInt[rs]; y ← Random.NextInt[rs] * aspectRatio;
FOR j: NAT IN [0..4) DO
xSgn, ySgn: INTEGER;
xSgn ← IF j < 1 OR j > 3 THEN -1 ELSE 1; ySgn ← IF j < 2 THEN 1 ELSE -1;
p[j].coord.sx ← MAX[0, MIN[ width-1, x + xSgn * Random.NextInt[rs] * 0.1]];
p[j].coord.sy ← MAX[0, MIN[ height-1, y+ySgn * Random.NextInt[rs] *0.1 *aspectRatio]];
p[j].shade.er ← Random.NextInt[rs] / width;
p[j].shade.eg ← Random.NextInt[rs] / width;
p[j].shade.eb ← Random.NextInt[rs] / width;
ENDLOOP;
[] ← context.class.displayPolygon[context, p];
ENDLOOP;
quadTime ← BasicTime.PulsesToSeconds[BasicTime.GetClockPulses[]] - time;
};
Scene Definition
SetScene1: PROCEDURE [] ~ {
IF scene1 = NIL THEN {
Reset[];
G3dRender.AddShapeFromFile[ context3d,
"CheckerBoard",        -- internal name
"CheckerBoard.shape",       -- file name
[-8.0, -8.0, -2.0]        -- position
];
G3dRender.SetColor[ FindShape[context3d, "CheckerBoard"], [.8, .4, .2] ]; -- add shading
G3dRender.AddShapeFromFile[ context3d,
           "Icosahedron", "Icosahedron.shape", [2.0, 0.0, 0.0] ];
G3dRender.SetColor[ FindShape[context3d, "Icosahedron"], [1., 1., 1.] ];
G3dRender.AddShapeFromFile[ context3d, "SoccerBall", "SoccerBall.shape", [-2.0, 0.0, 0.0] ];
G3dRender.SetColor[ FindShape[context3d, "SoccerBall"], [1., 1., 1.] ];
G3dRender.AddShapeFromFile[ context3d, "CutCube", "CutCube.shape", [0.0, 3.0, 0.0] ];
G3dRender.SetColor[ FindShape[context3d, "CutCube"], [.9, .8, .5] ];
scene1 ← NEW[ShapeSequenceRep[context3d.shapes.length]];
FOR i: NAT IN [0..context3d.shapes.length) DO
scene1[i] ← context3d.shapes[i];
ENDLOOP;
scene1.length ← context3d.shapes.length;
}
ELSE {
context3d.shapes ← NEW[ShapeSequenceRep[scene1.length]];
FOR i: NAT IN [0..scene1.length) DO
context3d.shapes[i] ← scene1[i];
ENDLOOP;
context3d.shapes.length ← scene1.length;
};
G3dRenderWithPixels.DepthBuffering[context3d, FALSE];
currentScene ← scene1;
};
SetScene2: PROCEDURE [] ~ {
IF scene2 = NIL THEN {
Reset[];
G3dRender.AddShapeFromFile[context3d, "ChampagneGlass", "ChampagneGlass.shape",
                      [2.0, 0.0, 0.0] ];
G3dRender.SetColor[ FindShape[context3d, "ChampagneGlass"], [1.,0.,1.] ];
G3dRender.SetTransmittance[ FindShape[context3d, "ChampagneGlass"], 0.8 ];
G3dRender.AddShapeFromFile[context3d, "UtahEgg", "UtahEgg.shape" ];
G3dShape.TransformShape[ shape: FindShape[context3d, "UtahEgg"], translate: [-2.,.0,.0],
        axis: [[0.0, 0.0, 0.0], [1.0, 1.0, 0.0]], rotation: 90.0 ];
G3dRender.SetColor[ FindShape[context3d, "UtahEgg"], [1.0, .8, .5] ];
G3dRender.AddShapeFromFile[context3d, "Banana", "Banana.shape" ];
G3dRender.SetColor[ FindShape[context3d, "Banana"], [1.0, .9, .1] ];
scene2 ← NEW[ShapeSequenceRep[context3d.shapes.length]];
FOR i: NAT IN [0..context3d.shapes.length) DO
scene2[i] ← context3d.shapes[i];
ENDLOOP;
scene2.length ← context3d.shapes.length;
}
ELSE {
context3d.shapes ← NEW[ShapeSequenceRep[scene2.length]];
FOR i: NAT IN [0..scene2.length) DO
context3d.shapes[i] ← scene2[i];
ENDLOOP;
context3d.shapes.length ← scene2.length;
};
G3dRenderWithPixels.DepthBuffering[context3d, FALSE];
currentScene ← scene2;
};
SetScene3: PROCEDURE [] ~ {
IF scene3 = NIL THEN {
Reset[];
G3dRender.AddShapeFromFile[context3d, "TeaPot", "TeaPotWithBot.shape", [0.0, 0.0, -1.25]];
scene3 ← NEW[ShapeSequenceRep[context3d.shapes.length]];
FOR i: NAT IN [0..context3d.shapes.length) DO
scene3[i] ← context3d.shapes[i];
ENDLOOP;
scene3.length ← context3d.shapes.length;
G3dRender.SetView[context3d, [3.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
}
ELSE {
context3d.shapes ← NEW[ShapeSequenceRep[scene3.length]];
FOR i: NAT IN [0..scene3.length) DO
context3d.shapes[i] ← scene3[i];
ENDLOOP;
context3d.shapes.length ← scene3.length;
};
currentScene ← scene3;
};
OneShape: PROCEDURE [] ~ {
Reset[];
G3dRender.AddShapeFromFile[context3d, "Shape0", ViewerTools.GetSelectionContents[] ];
G3dRender.SetView[context3d, [0.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
};
GetMouseRectangle: PROCEDURE[x, y: REAL] ~ {
IF mouseRectangle # NIL
THEN {
mouseRectangle.w ← x - mouseRectangle.x;
IF mouseRectangle.w < 0.0 THEN {
mouseRectangle.x ← mouseRectangle.x + mouseRectangle.w;
mouseRectangle.w ← -mouseRectangle.w;
};
mouseRectangle.h ← y - mouseRectangle.y;
IF mouseRectangle.h < 0.0 THEN {
mouseRectangle.y ← mouseRectangle.y + mouseRectangle.h;
mouseRectangle.h ← -mouseRectangle.h;
};
SELECT mouseWaiting FROM
$Window => {
mouseRectangle.x ← mouseRectangle.x - context3d.viewPort.x;
mouseRectangle.y ← mouseRectangle.y - context3d.viewPort.y;
mouseRectangle.w ← MIN[mouseRectangle.w, context3d.viewPort.w];
mouseRectangle.x ← 2.0 * (mouseRectangle.x / context3d.viewPort.w - 0.5); -- center
mouseRectangle.y ←
2.0 * (mouseRectangle.y - context3d.viewPort.h/2.0) / context3d.viewPort.w;
mouseRectangle.w ← 2.0 * mouseRectangle.w / context3d.viewPort.w;
mouseRectangle.h ← mouseRectangle.w * context3d.viewPort.h/context3d.viewPort.w;
windowStack ← CONS[ context3d.window^, windowStack ];
G3dRender.SetWindow[ context3d, mouseRectangle^ ];
mouseRectangle ← NIL;
mouseWaiting ← NIL;
};
$ViewPort => {
viewPortStack ← CONS[ context3d.viewPort^, viewPortStack ];
G3dRender.SetViewPort[ context3d, mouseRectangle^ ];
mouseRectangle ← NIL;
mouseWaiting ← NIL;
};
ENDCASE => SIGNAL G3dRender.Error[$MisMatch, "Unknown Mouse Atom"];
}
ELSE mouseRectangle ← NEW[ Rectangle ← [x, y, 0.0, 0.0] ];
};
GetWindow: PROCEDURE[] ~ {
mouseWaiting ← $Window
};
RestoreWindow: PROCEDURE[] ~ {
IF windowStack # NIL
THEN {
G3dRender.SetWindow[ context3d, windowStack.first ];
windowStack ← windowStack.rest;
}
ELSE SIGNAL G3dRender.Error[$MisMatch, "No previous window"];
};
GetViewPort: PROCEDURE [] ~ {
mouseWaiting ← $ViewPort
};
RestoreViewPort: PROCEDURE[] ~ {
IF viewPortStack # NIL
THEN {
G3dRender.SetViewPort[ context3d, viewPortStack.first ];
viewPortStack ← viewPortStack.rest;
}
ELSE SIGNAL G3dRender.Error[$MisMatch, "No previous viewport"];
};
ShowSinglePatch: PROCEDURE[ shapeNumber, patchNumber: NAT, keep: BOOLFALSE ] ~ {
context3d.props ← PutProp[
context3d.props, $SinglePatch, NEW[ IntegerPair ← [shapeNumber, patchNumber] ]
];
DoCommands[context3d, $Display];
IF NOT keep THEN context3d.props ← Atom.RemPropFromList[ context3d.props, $SinglePatch ];
};
ShowPatchesOnVtx: PROCEDURE[ shapeNumber, vtxNumber: NAT, keep: BOOLFALSE ] ~ {
context3d.props ← PutProp[
context3d.props, $SingleVtx, NEW[ IntegerPair ← [shapeNumber, vtxNumber] ]
];
DoCommands[context3d, $Display];
IF NOT keep THEN context3d.props ← Atom.RemPropFromList[ context3d.props, $SingleVtx ];
};
Test Patterns for filter evaluation
sumTable: REF RealSequence ← NIL;
discreteTable: BOOLEANFALSE;    -- TRUE if table made from discrete samples
testPatNumStripes, testPatRatio: REAL;
MakeCurves: PROC[numStripes: NAT, ratio: REAL] ~ {
testPatNumStripes ← numStripes; testPatRatio ← ratio;
IF context3d.viewer # NIL THEN {            -- do through viewer
context3d.class.drawInViewer[context3d, NEW[ImagerProcRec ← [ViewerMakeCurves, NIL]]];
};
};
ViewerMakeCurves: ImagerProc ~ {
DoMakeCurves: PROC[pixelMap: ImagerPixel.PixelMap] ~ {
Ceiling: PROC[number: REAL] RETURNS[ result: INTEGER ] ~ {
result ← Real.RoundI[number];
IF result < number THEN result ← result + 1;
};
DrawFilter: PROC[] ~ {
IF sumTable # NIL THEN {    -- draw convolution filter and pixel fiducials
by: NAT ← Real.RoundC[viewPort.h/12.0];
x: NAT ← Real.RoundC[viewPort.w/2.0 - 128.0];
Imager.SetColor[displayContext, Imager.black];
FOR i: NAT IN [0..4] DO        -- draw pixel fiducials
Imager.MaskVector[displayContext, [ x, by+10 ], [ x, by-10 ]];
Imager.MaskVector[displayContext, [ x+10, by ], [ x-10, by ]];
x ← x + 64;
ENDLOOP;
FOR i: NAT IN ( 0..sumTable.length ) DO   -- draw convolution filter
xx: NAT ← Real.RoundC[viewPort.w/2.0 + viewPort.x] + i - sumTable.length/2;
yy: INTEGER ← Real.FixI[ (sumTable[i] - sumTable[i-1]) * 35.0 * viewPort.h / 3.0 ];
IF yy # 0.0 AND i # 192 AND i # 64 THEN {
IF discreteTable THEN yy ← Real.FixI[ MIN[ .8 * viewPort.h/4.0, yy/15.0 ]];
yy ← by + yy;
Imager.MaskVector[displayContext, [ xx-1, by ], [ xx-1, yy ]];
Imager.MaskVector[displayContext, [ xx, by ], [ xx, yy ]];
Imager.MaskVector[displayContext, [ xx+1, by ], [ xx+1, yy ]];
};
ENDLOOP;
};
};
numStripes: REAL ← testPatNumStripes; ratio: REAL ← testPatRatio;
viewPort: Imager.Rectangle ← ImagerBackdoor.GetBounds[ imagerCtx ];
factor: REAL ← numStripes / viewPort.w;
pos: REF RealSequence ← NEW[ RealSequence[numStripes] ];
inc1: REF RealSequence ← NEW[ RealSequence[numStripes] ];
inc2: REF RealSequence ← NEW[ RealSequence[numStripes] ];
line: REF RealSequence ← NEW[ RealSequence[Real.Round[viewPort.w]+4] ];
scanLIne: ImagerPixel.PixelBuffer ← ImagerPixel.NewPixels[ pixelMap, 1, viewPort.w ];
maxValue: REAL ← 255.0;
viewPort.h ← viewPort.h * 3.0 / 4.0;  -- use just 3/4 of viewport
FOR i: NAT IN [ 0..numStripes ) DO   -- draw spacing curve
pos[i] ← ( 1.0 - ratio/(i+ratio) ) * viewPort.w * 1.0 / (1.0 - ratio / (numStripes+ratio));
inc1[i] ← Real.Float[(numStripes - i)] / numStripes;
inc2[i] ← -2 * inc1[i] / viewPort.h;
ENDLOOP;
Imager.DoSaveAll[imagerCtx, DrawFilter];
FOR y: NAT IN [ Real.RoundC[viewPort.h/3.0]
     .. Real.RoundC[viewPort.h*4.0/3.0] ) DO
lastX, thisX: REAL ← 0;
white: BOOLEANTRUE;
IF sumTable # NIL THEN FOR i: NAT IN [0..line.length) DO line[i] ← 0.0; ENDLOOP;
FOR i: NAT IN [ 0 .. numStripes ) DO
pxlValue: CARDINAL;
thisX ← pos[i];
IF white THEN pxlValue ← Real.FixC[maxValue] ELSE pxlValue ← 0;
IF sumTable = NIL THEN   -- no table, output plain
FOR x: NAT IN [ Real.FixC[lastX] .. Real.FixC[thisX] ) DO
IF x < Real.RoundC[viewPort.w]
THEN scanLine[x] ← pxlValue;
ENDLOOP
ELSE IF sumTable.length = 128 THEN {  -- 2-pixel table
tX: REAL ← thisX + 2;
lX: REAL ← lastX + 2;
IF white THEN {
lastIndex: NAT ← Real.FixC[lX * 64.0 - (Real.FixC[lX] * 64)];
thisIndex: NAT ← Real.FixC[tX * 64.0 - (Real.FixC[tX] * 64)];
loopStart: NAT ← Real.FixC[lX]-1;
loopEnd: NAT ← Real.FixC[tX];
FOR x: NAT IN [ loopStart .. loopEnd ] DO
SELECT x FROM
loopStart => {
line[x] ← line[x] + 1.0 - sumTable[lastIndex+64];
IF loopStart = loopEnd-1
THEN line[x] ← line[x] - (1.0 - sumTable[thisIndex+64]);
};
loopStart+1 => {
line[x] ← line[x] + 1.0 - sumTable[lastIndex];
IF loopStart = loopEnd-1
THEN line[x] ← line[x] - (1.0 - sumTable[thisIndex]);
IF loopStart = loopEnd-2
THEN line[x] ← line[x] - (1.0 - sumTable[thisIndex+64]);
};
loopEnd-1 => {
line[x] ← line[x] + sumTable[thisIndex+64];
};
loopEnd => {
line[x] ← line[x] + sumTable[thisIndex];
};
ENDCASE => line[x] ← 1.0;   
ENDLOOP;
}
}
ELSE IF sumTable.length = 256 THEN {  -- 4-pixel negative lobe table
tX: REAL ← thisX + 2;
lX: REAL ← lastX + 2;
IF white THEN {
lastIndex: NAT ← Real.FixC[lX * 64.0 - (Real.FixC[lX] * 64)];
thisIndex: NAT ← Real.FixC[tX * 64.0 - (Real.FixC[tX] * 64)];
loopStart: NAT ← Real.FixC[lX]-2;
loopEnd: NAT ← Real.FixC[tX]+1;
FOR x: NAT IN [ loopStart .. loopEnd ] DO
SELECT x FROM
loopStart => {
line[x] ← line[x] + sumTable[255] - sumTable[lastIndex+192];
IF loopStart = loopEnd-3    -- start and end in same pixel span
THEN line[x] ← line[x] - (sumTable[255] - sumTable[thisIndex+192]);
};
loopStart+1 => {
line[x] ← line[x] + sumTable[255]
     + sumTable[191] - sumTable[lastIndex+128];
IF loopStart = loopEnd-3    -- start and end in same pixel span
THEN line[x] ← line[x] - ( sumTable[255]
     + sumTable[191] - sumTable[thisIndex+128] );
IF loopStart = loopEnd-4    -- start and end in adjacent pixel spans
THEN line[x] ← line[x] - ( sumTable[255] - sumTable[thisIndex+192] );
};
loopStart+2 => {
line[x] ← line[x] + sumTable[255]
     + sumTable[191] - sumTable[lastIndex+64];
IF loopStart = loopEnd-3    -- start and end in same pixel span
THEN line[x] ← line[x] - ( sumTable[255]
        + sumTable[191] - sumTable[thisIndex+64] );
IF loopStart = loopEnd-4    -- start and end in adjacent pixel spans
THEN line[x] ← line[x] - ( sumTable[255]
        + sumTable[191] - sumTable[thisIndex+128] );
IF loopStart = loopEnd-5 -- start and end in pixels separated by one span
THEN line[x] ← line[x] - ( sumTable[255] - sumTable[thisIndex+192] );
};
loopStart+3 => {
line[x] ← line[x] + sumTable[255] + sumTable[191]
     + sumTable[63] - sumTable[lastIndex];
IF loopStart = loopEnd-3    -- start and end in same pixel span
THEN line[x] ← line[x] - ( sumTable[255] + sumTable[191]
        + sumTable[63] - sumTable[thisIndex] );
IF loopStart = loopEnd-4    -- start and end in adjacent pixel spans
THEN line[x] ← line[x] - ( sumTable[255]
        + sumTable[191] - sumTable[thisIndex+64] );
IF loopStart = loopEnd-5 -- start and end in pixels separated by one span
THEN line[x] ← line[x] - ( sumTable[255]
        + sumTable[191] - sumTable[thisIndex+128] );
IF loopStart = loopEnd-6 -- start and end in pixels separated by two spans
THEN line[x] ← line[x] - ( sumTable[255] - sumTable[thisIndex+192] );
};
loopEnd-3 => {
line[x] ← line[x] + sumTable[63] + sumTable[191]
     + sumTable[thisIndex+192];
};
loopEnd-2 => {
line[x] ← line[x] + sumTable[63] + sumTable[thisIndex+128];
};
loopEnd-1 => {
line[x] ← line[x] + sumTable[63] + sumTable[thisIndex+64];
};
loopEnd => {
line[x] ← line[x] + sumTable[thisIndex];
};
ENDCASE => line[x] ← 1.0;   
ENDLOOP;
}
};
white ← NOT white;
pos[i] ← pos[i] + inc1[i];
inc1[i] ← inc1[i] + inc2[i];
lastX ← thisX;
ENDLOOP;
IF sumTable # NIL THEN FOR i: NAT IN [2..Real.RoundC[viewPort.w]+2) DO
pixelValue: INTEGERMAX[ 0, Real.FixI[ MIN[maxValue, line[i]*(maxValue)] ] ];
scanLine[i-2] ← pixelValue;
ENDLOOP;
ImagerPixel.PutPixels[ self: pixelMap, pixels: scanLine,
       initIndex: [f: xStart, s: y], count: viewPort.w ];
ENDLOOP;
};
ImagerBackdoor.AccessBufferRectangle[imagerCtx, DoMakeCurves, context.viewPort^];
};
MakeNilTable: PROC[] ~ {
sumTable ← NIL;
};
MakeImpulseTable: PROC[] ~ {
sumTable ← NEW[ RealSequence[128] ];
FOR i: NAT IN [0 .. 64) DO sumTable[i] ← 0.0; ENDLOOP;
FOR i: NAT IN [64 .. 128) DO sumTable[i] ← 1.0; ENDLOOP;
discreteTable ← TRUE;
};
Make2ImpulseTable: PROC[] ~ {
sumTable ← NEW[ RealSequence[128] ];
FOR i: NAT IN [0 .. 48) DO sumTable[i] ← 0.0; ENDLOOP;
FOR i: NAT IN [48 .. 80) DO sumTable[i] ← 0.5; ENDLOOP;
FOR i: NAT IN [80 .. 128) DO sumTable[i] ← 1.0; ENDLOOP;
discreteTable ← TRUE;
};
Make4ImpulseTable: PROC[] ~ {
sumTable ← NEW[ RealSequence[128] ];
FOR i: NAT IN [0 .. 40) DO sumTable[i] ← 0.0; ENDLOOP;
FOR i: NAT IN [40 .. 56) DO sumTable[i] ← 0.25; ENDLOOP;
FOR i: NAT IN [56 .. 72) DO sumTable[i] ← 0.50; ENDLOOP;
FOR i: NAT IN [72 .. 88) DO sumTable[i] ← 0.75; ENDLOOP;
FOR i: NAT IN [88 .. 128) DO sumTable[i] ← 1.0; ENDLOOP;
discreteTable ← TRUE;
};
Make8ImpulseTable: PROC[] ~ {
sumTable ← NEW[ RealSequence[128] ];
FOR i: NAT IN [0 .. 36) DO sumTable[i] ← 0.000; ENDLOOP;
FOR i: NAT IN [36 .. 44) DO sumTable[i] ← 0.125; ENDLOOP;
FOR i: NAT IN [44 .. 52) DO sumTable[i] ← 0.250; ENDLOOP;
FOR i: NAT IN [52 .. 60) DO sumTable[i] ← 0.375; ENDLOOP;
FOR i: NAT IN [60 .. 68) DO sumTable[i] ← 0.500; ENDLOOP;
FOR i: NAT IN [68 .. 76) DO sumTable[i] ← 0.625; ENDLOOP;
FOR i: NAT IN [76 .. 84) DO sumTable[i] ← 0.750; ENDLOOP;
FOR i: NAT IN [84 .. 92) DO sumTable[i] ← 0.875; ENDLOOP;
FOR i: NAT IN [92 .. 128) DO sumTable[i] ← 1.000; ENDLOOP;
discreteTable ← TRUE;
};
MakeWtd8ImpulseTable: PROC[] ~ {
sumTable ← NEW[ RealSequence[128] ];
FOR i: NAT IN [0 .. 8) DO sumTable[i] ← 0.0/32.0; ENDLOOP;
FOR i: NAT IN [8 .. 24) DO sumTable[i] ← 1.0/32.0; ENDLOOP;
FOR i: NAT IN [24 .. 40) DO sumTable[i] ← 4.0/32.0; ENDLOOP;
FOR i: NAT IN [40 .. 56) DO sumTable[i] ← 9.0/32.0; ENDLOOP;
FOR i: NAT IN [56 .. 72) DO sumTable[i] ← 16.0/32.0; ENDLOOP;
FOR i: NAT IN [72 .. 88) DO sumTable[i] ← 23.0/32.0; ENDLOOP;
FOR i: NAT IN [88 .. 104) DO sumTable[i] ← 28.0/32.0; ENDLOOP;
FOR i: NAT IN [104 .. 120) DO sumTable[i] ← 31.0/32.0; ENDLOOP;
FOR i: NAT IN [120 .. 128) DO sumTable[i] ← 32.0/32.0; ENDLOOP;
discreteTable ← TRUE;
};
MakeWtd16ImpulseTable: PROC[] ~ {
sum: REAL ← 0;
lastPos: NAT ← 0;
thisPos: NAT ← 4;
sumTable ← NEW[ RealSequence[128] ];
FOR i: NAT IN [0..16) DO
j: NAT ← 8 - ABS[8 - i]; IF i > 7 THEN j ← j -1;
FOR k: NAT IN [lastPos .. thisPos) DO sumTable[k] ← sum; ENDLOOP;
sum ← sum + (j*2 + 1)/128.0;
lastPos ← thisPos;
thisPos ← thisPos + 8;
ENDLOOP;
FOR k: NAT IN [lastPos .. 128) DO sumTable[k] ← 1.0; ENDLOOP;
discreteTable ← TRUE;
};
MakeBoxTable: PROC[] ~ {
sumTable ← NEW[ RealSequence[128] ];
FOR i: NAT IN [0 .. 32) DO sumTable[i] ← 0.0; ENDLOOP;
FOR i: NAT IN [32 .. 96) DO sumTable[i] ← (i-32) / 64.0; ENDLOOP;
FOR i: NAT IN [96 .. 128) DO sumTable[i] ← 1.0; ENDLOOP;
discreteTable ← FALSE;
};
MakeTriangleTable: PROC[] ~ {
Calculates the integral over the pyramid function, equal to the left half of a parabolic window or B-spline basis function
sumTable ← NEW[ RealSequence[128] ];
FOR i: NAT IN [0..64) DO
t: REAL ← i * 1.0 / 64.0;
sumTable[i]     ← Sqr[t] / 2.;
sumTable[i + 64] ← 1. - Sqr[1. - t] / 2.;
ENDLOOP;
discreteTable ← FALSE;
};
MakeGaussTable: PROC[spread: REAL] ~ {
Calculates the integral over the central lobe of the sinc function
sumTable ← NEW[ RealSequence[128] ];
FOR i: NAT IN [0..64) DO
t: REAL ← (i * 1.0 / 64.0);
sumTable[64 + i] ← RealFns.Exp[-spread*t*t];
sumTable[63 - i] ← sumTable[64 + i];
ENDLOOP;
FOR i: NAT IN (0..128) DO
sumTable[i] ← sumTable[i-1] + sumTable[i];
ENDLOOP;
FOR i: NAT IN [0..128) DO
sumTable[i] ← sumTable[i] / sumTable[127];
ENDLOOP;
discreteTable ← FALSE;
};
MakeSinc2Table: PROC[] ~ {
Calculates the integral over the central lobe of the sinc function
sumTable ← NEW[ RealSequence[128] ];
FOR i: NAT IN [0..64) DO
t: REAL ← (1.0 - i * 1.0 / 64.0) * 3.1416;
sumTable[i] ← RealFns.Sin[t] / t;
sumTable[127 - i] ← sumTable[i];
ENDLOOP;
FOR i: NAT IN (0..128) DO
sumTable[i] ← sumTable[i-1] + sumTable[i];
ENDLOOP;
FOR i: NAT IN [0..128) DO
sumTable[i] ← sumTable[i] / sumTable[127];
ENDLOOP;
discreteTable ← FALSE;
};
MakeSinc4Table: PROC[weighted, sincWeighting: BOOLEANTRUE] ~ {
Calculates the integral over the inner 3 lobes of the sinc function weighted by the inner lobe stretched by 2 or weighted by a triangle
completeSum: REAL;
sumTable ← NEW[ RealSequence[256] ];
FOR i: NAT IN [0..128) DO
t: REAL ← (2.0 - i * 1.0 / 64.0) * 3.1416;
sumTable[i] ← RealFns.Sin[t] / t;
IF weighted THEN
IF sincWeighting THEN sumTable[i] ← (RealFns.Sin[t/2.0] / (t/2.0)) * sumTable[i]
      ELSE sumTable[i] ← (i * 1.0 / 128.0) * sumTable[i]; -- triangle weight
sumTable[255 - i] ← sumTable[i];
ENDLOOP;
FOR i: NAT IN (0..64) DO
sumTable[i] ← sumTable[i-1] + sumTable[i];
sumTable[i+192] ← sumTable[i+192-1] + sumTable[i+192];
ENDLOOP;
FOR i: NAT IN (0..128) DO
sumTable[i+64] ← sumTable[i+64-1] + sumTable[i+64];
ENDLOOP;
completeSum ← sumTable[63] + sumTable[191] + sumTable[255];
FOR i: NAT IN [0..256) DO
sumTable[i] ← sumTable[i] / completeSum;
ENDLOOP;
sumTable[0] ← sumTable[0];
discreteTable ← FALSE;
};
Frame Generation and Animation
Orbit: PROCEDURE [] ~ {
IF DemoErrorMessages[] THEN RETURN;
animation ← TRUE;
G3dAnimationSupport.Orbit[
context: context3d,
lookingFrom: context3d.eyePoint,
lookingAt: context3d.lookAt,
axis: [0.3, 0.0, 1.0],
base: context3d.lookAt,
framesPerRev: 53
! UNWIND => animation ← FALSE
];
animation ← FALSE;
};
MakeFrame: PROC ~ {
context3d.class.render[context3d ];      -- clear and make new frame
};
ShowPixels: PROC ~ {
context3d.class.drawInViewer[ context3d,
        NEW
[ImagerProcRec ← [G3dColorDisplaySupport.StuffBuf, NIL]]
        ];
};
ShowShapes: PROC ~ {
G3dSortandDisplay.ShowShapes[context3d ];     -- show shapes without clearing
};
movieFileRoot: Rope.ROPE;
movieFileLog: Rope.ROPE;
movieFileLogTime: BasicTime.GMT;
movieNumFiles: NAT;
movieStart: NAT;
movieLabeledFrames: BOOLEAN;
SetUpMovie: PROC[fileRoot: Rope.ROPE, numFiles, start: NAT ← 0, labels: BOOLEANFALSE] ~ {
fileUpdated: BOOLEANFALSE;
IF frames # NIL AND Rope.Equal[movieFileRoot, fileRoot]
THEN {
latestTime: BasicTime.GMT;
[created: latestTime] ← FS.FileInfo[ G3dRender.GetLogFileName[fileRoot] ];
IF BasicTime.Period[from: movieFileLogTime, to: latestTime] > 0
THEN fileUpdated ← TRUE;
}
ELSE frames ← NIL;
IF frames = NIL OR fileUpdated THEN {
movieFileRoot ← fileRoot;
[fullFName: movieFileLog, created: movieFileLogTime] ← FS.FileInfo[
G3dRender.GetLogFileName[fileRoot]
];
movieNumFiles ← numFiles;
movieStart ← start;
movieLabeledFrames ← labels;
frames ← NIL;
};
};
frameRate: INTEGER ← 6;  -- playback rate in frames/second, negative plays backwards
PlayBackbyFrame: CedarProcess.ForkableProc ~ {
CleanUp: PROC ~ {
G3dRender.SetViewPort[ context3d, cachedViewPort ];
IF context3d.class.displayType = $PseudoColor
THEN G3dColorDisplaySupport.LoadStd8BitClrMap[context3d.terminal];
};
size, posn: Pair;
G3dSortandDisplay.ValidateContext[context3d];
cachedViewPort ← context3d.viewPort^;
context3d.class.loadBackground[context3d];
IF frames = NIL THEN {
frames ← G3dAnimationSupport.CacheNumberedAISFiles[
context3d, movieFileRoot, movieNumFiles, movieStart, movieLabeledFrames
];
};
Construct viewPort from cached frame size
IF context3d.stopMe^ THEN { CleanUp[]; RETURN[]; };
size ← [frames[0].box.max.f - frames[0].box.min.f, frames[0].box.max.s - frames[0].box.min.s];
posn ← IF context3d.viewer # NIL
THEN [ context3d.viewer.cx + (context3d.viewer.cw/2 - size.x/2),
context3d.viewer.cy + (context3d.viewer.ch/2 - size.y/2) ]
ELSE [ context3d.pixels.box.max.f/2 - size.x/2, context3d.pixels.box.max.s/2 - size.y/2 ];
G3dRender.SetViewPort[ context3d, [ posn.x, posn.y, size.x, size.y ] ];
IF context3d.viewer # NIL THEN ThreeDViewer.ViewerUpdate[context3d]; -- install viewport
context3d.class.draw2DRope[context3d, "Control frame rate using left button", [140, 50] ];
WHILE TRUE DO
ENABLE UNWIND => CleanUp[];
IF context3d.stopMe^ THEN { CleanUp[]; RETURN[]; };
G3dAnimationSupport.ShowNextAISCacheFrame[context3d, frames, frameRate];
CedarProcess.CheckAbort[];
ENDLOOP;
};
Commander.Register["ThreeDDemo", ThreeDDemoCommand, "\nDemonstrate ThreeDWorld"];
END.